openai/openai-python

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.28.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

README.md

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