openai/openai-python

Public

mirrored from https://github.com/openai/openai-pythonAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.54.4

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

README.md

723lines · modecode

1# OpenAI Python API library
2
3[![PyPI version](https://img.shields.io/pypi/v/openai.svg)](https://pypi.org/project/openai/)
4
5The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.8+
6application. The library includes type definitions for all request params and response fields,
7and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
8
9It is generated from our [OpenAPI specification](https://github.com/openai/openai-openapi) with [Stainless](https://stainlessapi.com/).
10
11## Documentation
12
13The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs). The full API of this library can be found in [api.md](api.md).
14
15## Installation
16
17> [!IMPORTANT]
18> The SDK was rewritten in v1, which was released November 6th 2023. See the [v1 migration guide](https://github.com/openai/openai-python/discussions/742), which includes scripts to automatically update your code.
19
20```sh
21# install from PyPI
22pip install openai
23```
24
25## Usage
26
27The full API of this library can be found in [api.md](api.md).
28
29```python
30import os
31from openai import OpenAI
32
33client = OpenAI(
34 api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted
35)
36
37chat_completion = client.chat.completions.create(
38 messages=[
39 {
40 "role": "user",
41 "content": "Say this is a test",
42 }
43 ],
44 model="gpt-4o",
45)
46```
47
48While you can provide an `api_key` keyword argument,
49we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)
50to add `OPENAI_API_KEY="My API Key"` to your `.env` file
51so that your API Key is not stored in source control.
52
53### Vision
54
55With a hosted image:
56
57```python
58response = client.chat.completions.create(
59 model="gpt-4o-mini",
60 messages=[
61 {
62 "role": "user",
63 "content": [
64 {"type": "text", "text": prompt},
65 {
66 "type": "image_url",
67 "image_url": {"url": f"{img_url}"},
68 },
69 ],
70 }
71 ],
72)
73```
74
75With the image as a base64 encoded string:
76
77```python
78response = client.chat.completions.create(
79 model="gpt-4o-mini",
80 messages=[
81 {
82 "role": "user",
83 "content": [
84 {"type": "text", "text": prompt},
85 {
86 "type": "image_url",
87 "image_url": {"url": f"data:{img_type};base64,{img_b64_str}"},
88 },
89 ],
90 }
91 ],
92)
93```
94
95### Polling Helpers
96
97When interacting with the API some actions such as starting a Run and adding files to vector stores are asynchronous and take time to complete. The SDK includes
98helper functions which will poll the status until it reaches a terminal state and then return the resulting object.
99If an API method results in an action that could benefit from polling there will be a corresponding version of the
100method ending in '\_and_poll'.
101
102For instance to create a Run and poll until it reaches a terminal state you can run:
103
104```python
105run = client.beta.threads.runs.create_and_poll(
106 thread_id=thread.id,
107 assistant_id=assistant.id,
108)
109```
110
111More information on the lifecycle of a Run can be found in the [Run Lifecycle Documentation](https://platform.openai.com/docs/assistants/how-it-works/run-lifecycle)
112
113### Bulk Upload Helpers
114
115When creating and interacting with vector stores, you can use polling helpers to monitor the status of operations.
116For convenience, we also provide a bulk upload helper to allow you to simultaneously upload several files at once.
117
118```python
119sample_files = [Path("sample-paper.pdf"), ...]
120
121batch = await client.vector_stores.file_batches.upload_and_poll(
122 store.id,
123 files=sample_files,
124)
125```
126
127### Streaming Helpers
128
129The SDK also includes helpers to process streams and handle incoming events.
130
131```python
132with client.beta.threads.runs.stream(
133 thread_id=thread.id,
134 assistant_id=assistant.id,
135 instructions="Please address the user as Jane Doe. The user has a premium account.",
136) as stream:
137 for event in stream:
138 # Print the text from text delta events
139 if event.type == "thread.message.delta" and event.data.delta.content:
140 print(event.data.delta.content[0].text)
141```
142
143More information on streaming helpers can be found in the dedicated documentation: [helpers.md](helpers.md)
144
145## Async usage
146
147Simply import `AsyncOpenAI` instead of `OpenAI` and use `await` with each API call:
148
149```python
150import os
151import asyncio
152from openai import AsyncOpenAI
153
154client = AsyncOpenAI(
155 api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted
156)
157
158
159async def main() -> None:
160 chat_completion = await client.chat.completions.create(
161 messages=[
162 {
163 "role": "user",
164 "content": "Say this is a test",
165 }
166 ],
167 model="gpt-4o",
168 )
169
170
171asyncio.run(main())
172```
173
174Functionality between the synchronous and asynchronous clients is otherwise identical.
175
176## Streaming responses
177
178We provide support for streaming responses using Server Side Events (SSE).
179
180```python
181from openai import OpenAI
182
183client = OpenAI()
184
185stream = client.chat.completions.create(
186 messages=[
187 {
188 "role": "user",
189 "content": "Say this is a test",
190 }
191 ],
192 model="gpt-4o",
193 stream=True,
194)
195for chunk in stream:
196 print(chunk.choices[0].delta.content or "", end="")
197```
198
199The async client uses the exact same interface.
200
201```python
202import asyncio
203from openai import AsyncOpenAI
204
205client = AsyncOpenAI()
206
207
208async def main():
209 stream = await client.chat.completions.create(
210 model="gpt-4",
211 messages=[{"role": "user", "content": "Say this is a test"}],
212 stream=True,
213 )
214 async for chunk in stream:
215 print(chunk.choices[0].delta.content or "", end="")
216
217
218asyncio.run(main())
219```
220
221## Module-level client
222
223> [!IMPORTANT]
224> We highly recommend instantiating client instances instead of relying on the global client.
225
226We also expose a global client instance that is accessible in a similar fashion to versions prior to v1.
227
228```py
229import openai
230
231# optional; defaults to `os.environ['OPENAI_API_KEY']`
232openai.api_key = '...'
233
234# all client options can be configured just like the `OpenAI` instantiation counterpart
235openai.base_url = "https://..."
236openai.default_headers = {"x-foo": "true"}
237
238completion = openai.chat.completions.create(
239 model="gpt-4o",
240 messages=[
241 {
242 "role": "user",
243 "content": "How do I output all files in a directory using Python?",
244 },
245 ],
246)
247print(completion.choices[0].message.content)
248```
249
250The API is the exact same as the standard client instance-based API.
251
252This is intended to be used within REPLs or notebooks for faster iteration, **not** in application code.
253
254We recommend that you always instantiate a client (e.g., with `client = OpenAI()`) in application code because:
255
256- It can be difficult to reason about where client options are configured
257- It's not possible to change certain client options without potentially causing race conditions
258- It's harder to mock for testing purposes
259- It's not possible to control cleanup of network connections
260
261## Using types
262
263Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:
264
265- Serializing back into JSON, `model.to_json()`
266- Converting to a dictionary, `model.to_dict()`
267
268Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.
269
270## Pagination
271
272List methods in the OpenAI API are paginated.
273
274This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually:
275
276```python
277from openai import OpenAI
278
279client = OpenAI()
280
281all_jobs = []
282# Automatically fetches more pages as needed.
283for job in client.fine_tuning.jobs.list(
284 limit=20,
285):
286 # Do something with job here
287 all_jobs.append(job)
288print(all_jobs)
289```
290
291Or, asynchronously:
292
293```python
294import asyncio
295from openai import AsyncOpenAI
296
297client = AsyncOpenAI()
298
299
300async def main() -> None:
301 all_jobs = []
302 # Iterate through items across all pages, issuing requests as needed.
303 async for job in client.fine_tuning.jobs.list(
304 limit=20,
305 ):
306 all_jobs.append(job)
307 print(all_jobs)
308
309
310asyncio.run(main())
311```
312
313Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages:
314
315```python
316first_page = await client.fine_tuning.jobs.list(
317 limit=20,
318)
319if first_page.has_next_page():
320 print(f"will fetch next page using these details: {first_page.next_page_info()}")
321 next_page = await first_page.get_next_page()
322 print(f"number of items we just fetched: {len(next_page.data)}")
323
324# Remove `await` for non-async usage.
325```
326
327Or just work directly with the returned data:
328
329```python
330first_page = await client.fine_tuning.jobs.list(
331 limit=20,
332)
333
334print(f"next page cursor: {first_page.after}") # => "next page cursor: ..."
335for job in first_page.data:
336 print(job.id)
337
338# Remove `await` for non-async usage.
339```
340
341## Nested params
342
343Nested parameters are dictionaries, typed using `TypedDict`, for example:
344
345```python
346from openai import OpenAI
347
348client = OpenAI()
349
350completion = client.chat.completions.create(
351 messages=[
352 {
353 "role": "user",
354 "content": "Can you generate an example json object describing a fruit?",
355 }
356 ],
357 model="gpt-4o",
358 response_format={"type": "json_object"},
359)
360```
361
362## File uploads
363
364Request parameters that correspond to file uploads can be passed as `bytes`, a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`.
365
366```python
367from pathlib import Path
368from openai import OpenAI
369
370client = OpenAI()
371
372client.files.create(
373 file=Path("input.jsonl"),
374 purpose="fine-tune",
375)
376```
377
378The async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically.
379
380## Handling errors
381
382When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `openai.APIConnectionError` is raised.
383
384When the API returns a non-success status code (that is, 4xx or 5xx
385response), a subclass of `openai.APIStatusError` is raised, containing `status_code` and `response` properties.
386
387All errors inherit from `openai.APIError`.
388
389```python
390import openai
391from openai import OpenAI
392
393client = OpenAI()
394
395try:
396 client.fine_tuning.jobs.create(
397 model="gpt-4o",
398 training_file="file-abc123",
399 )
400except openai.APIConnectionError as e:
401 print("The server could not be reached")
402 print(e.__cause__) # an underlying Exception, likely raised within httpx.
403except openai.RateLimitError as e:
404 print("A 429 status code was received; we should back off a bit.")
405except openai.APIStatusError as e:
406 print("Another non-200-range status code was received")
407 print(e.status_code)
408 print(e.response)
409```
410
411Error codes are as followed:
412
413| Status Code | Error Type |
414| ----------- | -------------------------- |
415| 400 | `BadRequestError` |
416| 401 | `AuthenticationError` |
417| 403 | `PermissionDeniedError` |
418| 404 | `NotFoundError` |
419| 422 | `UnprocessableEntityError` |
420| 429 | `RateLimitError` |
421| >=500 | `InternalServerError` |
422| N/A | `APIConnectionError` |
423
424## Request IDs
425
426> For more information on debugging requests, see [these docs](https://platform.openai.com/docs/api-reference/debugging-requests)
427
428All object responses in the SDK provide a `_request_id` property which is added from the `x-request-id` response header so that you can quickly log failing requests and report them back to OpenAI.
429
430```python
431completion = await client.chat.completions.create(
432 messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-4"
433)
434print(completion._request_id) # req_123
435```
436
437Note that unlike other properties that use an `_` prefix, the `_request_id` property
438*is* public. Unless documented otherwise, *all* other `_` prefix properties,
439methods and modules are *private*.
440
441
442### Retries
443
444Certain errors are automatically retried 2 times by default, with a short exponential backoff.
445Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,
446429 Rate Limit, and >=500 Internal errors are all retried by default.
447
448You can use the `max_retries` option to configure or disable retry settings:
449
450```python
451from openai import OpenAI
452
453# Configure the default for all requests:
454client = OpenAI(
455 # default is 2
456 max_retries=0,
457)
458
459# Or, configure per-request:
460client.with_options(max_retries=5).chat.completions.create(
461 messages=[
462 {
463 "role": "user",
464 "content": "How can I get the name of the current day in JavaScript?",
465 }
466 ],
467 model="gpt-4o",
468)
469```
470
471### Timeouts
472
473By default requests time out after 10 minutes. You can configure this with a `timeout` option,
474which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object:
475
476```python
477from openai import OpenAI
478
479# Configure the default for all requests:
480client = OpenAI(
481 # 20 seconds (default is 10 minutes)
482 timeout=20.0,
483)
484
485# More granular control:
486client = OpenAI(
487 timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
488)
489
490# Override per-request:
491client.with_options(timeout=5.0).chat.completions.create(
492 messages=[
493 {
494 "role": "user",
495 "content": "How can I list all files in a directory using Python?",
496 }
497 ],
498 model="gpt-4o",
499)
500```
501
502On timeout, an `APITimeoutError` is thrown.
503
504Note that requests that time out are [retried twice by default](#retries).
505
506## Advanced
507
508### Logging
509
510We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.
511
512You can enable logging by setting the environment variable `OPENAI_LOG` to `debug`.
513
514```shell
515$ export OPENAI_LOG=debug
516```
517
518### How to tell whether `None` means `null` or missing
519
520In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`:
521
522```py
523if response.my_field is None:
524 if 'my_field' not in response.model_fields_set:
525 print('Got json like {}, without a "my_field" key present at all.')
526 else:
527 print('Got json like {"my_field": null}.')
528```
529
530### Accessing raw response data (e.g. headers)
531
532The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g.,
533
534```py
535from openai import OpenAI
536
537client = OpenAI()
538response = client.chat.completions.with_raw_response.create(
539 messages=[{
540 "role": "user",
541 "content": "Say this is a test",
542 }],
543 model="gpt-4o",
544)
545print(response.headers.get('X-My-Header'))
546
547completion = response.parse() # get the object that `chat.completions.create()` would have returned
548print(completion)
549```
550
551These methods return an [`LegacyAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version.
552
553For the sync client this will mostly be the same with the exception
554of `content` & `text` will be methods instead of properties. In the
555async client, all methods will be async.
556
557A migration script will be provided & the migration in general should
558be smooth.
559
560#### `.with_streaming_response`
561
562The above interface eagerly reads the full response body when you make the request, which may not always be what you want.
563
564To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods.
565
566As such, `.with_streaming_response` methods return a different [`APIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object, and the async client returns an [`AsyncAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object.
567
568```python
569with client.chat.completions.with_streaming_response.create(
570 messages=[
571 {
572 "role": "user",
573 "content": "Say this is a test",
574 }
575 ],
576 model="gpt-4o",
577) as response:
578 print(response.headers.get("X-My-Header"))
579
580 for line in response.iter_lines():
581 print(line)
582```
583
584The context manager is required so that the response will reliably be closed.
585
586### Making custom/undocumented requests
587
588This library is typed for convenient access to the documented API.
589
590If you need to access undocumented endpoints, params, or response properties, the library can still be used.
591
592#### Undocumented endpoints
593
594To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other
595http verbs. Options on the client will be respected (such as retries) will be respected when making this
596request.
597
598```py
599import httpx
600
601response = client.post(
602 "/foo",
603 cast_to=httpx.Response,
604 body={"my_param": True},
605)
606
607print(response.headers.get("x-foo"))
608```
609
610#### Undocumented request params
611
612If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request
613options.
614
615#### Undocumented response properties
616
617To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You
618can also get all the extra fields on the Pydantic model as a dict with
619[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra).
620
621### Configuring the HTTP client
622
623You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including:
624
625- Support for proxies
626- Custom transports
627- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality
628
629```python
630from openai import OpenAI, DefaultHttpxClient
631
632client = OpenAI(
633 # Or use the `OPENAI_BASE_URL` env var
634 base_url="http://my.test.server.example.com:8083/v1",
635 http_client=DefaultHttpxClient(
636 proxies="http://my.test.proxy.example.com",
637 transport=httpx.HTTPTransport(local_address="0.0.0.0"),
638 ),
639)
640```
641
642You can also customize the client on a per-request basis by using `with_options()`:
643
644```python
645client.with_options(http_client=DefaultHttpxClient(...))
646```
647
648### Managing HTTP resources
649
650By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting.
651
652## Microsoft Azure OpenAI
653
654To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the `AzureOpenAI`
655class instead of the `OpenAI` class.
656
657> [!IMPORTANT]
658> The Azure API shape differs from the core API shape which means that the static types for responses / params
659> won't always be correct.
660
661```py
662from openai import AzureOpenAI
663
664# gets the API Key from environment variable AZURE_OPENAI_API_KEY
665client = AzureOpenAI(
666 # https://learn.microsoft.com/azure/ai-services/openai/reference#rest-api-versioning
667 api_version="2023-07-01-preview",
668 # https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource
669 azure_endpoint="https://example-endpoint.openai.azure.com",
670)
671
672completion = client.chat.completions.create(
673 model="deployment-name", # e.g. gpt-35-instant
674 messages=[
675 {
676 "role": "user",
677 "content": "How do I output all files in a directory using Python?",
678 },
679 ],
680)
681print(completion.to_json())
682```
683
684In addition to the options provided in the base `OpenAI` client, the following options are provided:
685
686- `azure_endpoint` (or the `AZURE_OPENAI_ENDPOINT` environment variable)
687- `azure_deployment`
688- `api_version` (or the `OPENAI_API_VERSION` environment variable)
689- `azure_ad_token` (or the `AZURE_OPENAI_AD_TOKEN` environment variable)
690- `azure_ad_token_provider`
691
692An example of using the client with Microsoft Entra ID (formerly known as Azure Active Directory) can be found [here](https://github.com/openai/openai-python/blob/main/examples/azure_ad.py).
693
694## Versioning
695
696This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
697
6981. Changes that only affect static types, without breaking runtime behavior.
6992. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals)_.
7003. Changes that we do not expect to impact the vast majority of users in practice.
701
702We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
703
704We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-python/issues) with questions, bugs, or suggestions.
705
706### Determining the installed version
707
708If you've upgraded to the latest version but aren't seeing any new features you were expecting then your python environment is likely still using an older version.
709
710You can determine the version that is being used at runtime with:
711
712```py
713import openai
714print(openai.__version__)
715```
716
717## Requirements
718
719Python 3.8 or higher.
720
721## Contributing
722
723See [the contributing documentation](./CONTRIBUTING.md).