openai/openai-python

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.50.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

README.md

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