openai/openai-python

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.99.7

Branches

Tags

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

Clone

HTTPS

Download ZIP

README.md

859lines · modeblame

08b8179aDavid Schnurr2 years ago1# OpenAI Python API library
3c6d4cd6Greg Brockman5 years ago2
db5c3504stainless-app[bot]11 months ago3<!-- prettier-ignore -->
4[![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/)
3c6d4cd6Greg Brockman5 years ago5
cb88c2f0stainless-app[bot]1 years ago6The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.8+
08b8179aDavid Schnurr2 years ago7application. The library includes type definitions for all request params and response fields,
8and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
9
10It is generated from our [OpenAPI specification](https://github.com/openai/openai-openapi) with [Stainless](https://stainlessapi.com/).
11
12## Documentation
13
2954945eRobert Craigie1 years ago14The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs/api-reference). The full API of this library can be found in [api.md](api.md).
3c6d4cd6Greg Brockman5 years ago15
16## Installation
17
18```sh
1879c97aStainless Bot2 years ago19# install from PyPI
6d217096Robert Craigie2 years ago20pip install openai
3c6d4cd6Greg Brockman5 years ago21```
22
08b8179aDavid Schnurr2 years ago23## Usage
24
986f3128Stainless Bot2 years ago25The full API of this library can be found in [api.md](api.md).
376dd199Logan Kilpatrick2 years ago26
2954945eRobert Craigie1 years ago27The primary API for interacting with OpenAI models is the [Responses API](https://platform.openai.com/docs/api-reference/responses). You can generate text from the model with the code below.
28
376dd199Logan Kilpatrick2 years ago29```python
fb5ba01cStainless Bot2 years ago30import os
08b8179aDavid Schnurr2 years ago31from openai import OpenAI
32
33client = OpenAI(
2954945eRobert Craigie1 years ago34# This is the default and can be omitted
35api_key=os.environ.get("OPENAI_API_KEY"),
36)
37
38response = client.responses.create(
39model="gpt-4o",
40instructions="You are a coding assistant that talks like a pirate.",
41input="How do I check if a Python object is an instance of a class?",
08b8179aDavid Schnurr2 years ago42)
43
2954945eRobert Craigie1 years ago44print(response.output_text)
45```
46
47The previous standard (supported indefinitely) for generating text is the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat). You can use that API to generate text from the model with the code below.
48
49```python
50from openai import OpenAI
51
52client = OpenAI()
53
54completion = client.chat.completions.create(
55model="gpt-4o",
08b8179aDavid Schnurr2 years ago56messages=[
2954945eRobert Craigie1 years ago57{"role": "developer", "content": "Talk like a pirate."},
08b8179aDavid Schnurr2 years ago58{
59"role": "user",
2954945eRobert Craigie1 years ago60"content": "How do I check if a Python object is an instance of a class?",
61},
08b8179aDavid Schnurr2 years ago62],
63)
2954945eRobert Craigie1 years ago64
65print(completion.choices[0].message.content)
376dd199Logan Kilpatrick2 years ago66```
67
08b8179aDavid Schnurr2 years ago68While you can provide an `api_key` keyword argument,
69we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)
70to add `OPENAI_API_KEY="My API Key"` to your `.env` file
2954945eRobert Craigie1 years ago71so that your API key is not stored in source control.
72[Get an API key here](https://platform.openai.com/settings/organization/api-keys).
3c6d4cd6Greg Brockman5 years ago73
192b8f2bDan Corin1 years ago74### Vision
75
2954945eRobert Craigie1 years ago76With an image URL:
192b8f2bDan Corin1 years ago77
78```python
2954945eRobert Craigie1 years ago79prompt = "What is in this image?"
80img_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/2023_06_08_Raccoon1.jpg/1599px-2023_06_08_Raccoon1.jpg"
81
82response = client.responses.create(
192b8f2bDan Corin1 years ago83model="gpt-4o-mini",
2954945eRobert Craigie1 years ago84input=[
192b8f2bDan Corin1 years ago85{
86"role": "user",
87"content": [
2954945eRobert Craigie1 years ago88{"type": "input_text", "text": prompt},
89{"type": "input_image", "image_url": f"{img_url}"},
192b8f2bDan Corin1 years ago90],
91}
92],
93)
94```
95
96With the image as a base64 encoded string:
97
98```python
2954945eRobert Craigie1 years ago99import base64
100from openai import OpenAI
101
102client = OpenAI()
103
104prompt = "What is in this image?"
105with open("path/to/image.png", "rb") as image_file:
106b64_image = base64.b64encode(image_file.read()).decode("utf-8")
107
108response = client.responses.create(
192b8f2bDan Corin1 years ago109model="gpt-4o-mini",
2954945eRobert Craigie1 years ago110input=[
192b8f2bDan Corin1 years ago111{
112"role": "user",
113"content": [
2954945eRobert Craigie1 years ago114{"type": "input_text", "text": prompt},
115{"type": "input_image", "image_url": f"data:image/png;base64,{b64_image}"},
192b8f2bDan Corin1 years ago116],
117}
118],
119)
120```
121
08b8179aDavid Schnurr2 years ago122## Async usage
3c6d4cd6Greg Brockman5 years ago123
08b8179aDavid Schnurr2 years ago124Simply import `AsyncOpenAI` instead of `OpenAI` and use `await` with each API call:
ede08829Jakub Roztocil3 years ago125
08b8179aDavid Schnurr2 years ago126```python
fb5ba01cStainless Bot2 years ago127import os
08b8179aDavid Schnurr2 years ago128import asyncio
129from openai import AsyncOpenAI
ede08829Jakub Roztocil3 years ago130
08b8179aDavid Schnurr2 years ago131client = AsyncOpenAI(
2954945eRobert Craigie1 years ago132# This is the default and can be omitted
133api_key=os.environ.get("OPENAI_API_KEY"),
08b8179aDavid Schnurr2 years ago134)
ede08829Jakub Roztocil3 years ago135
136
08b8179aDavid Schnurr2 years ago137async def main() -> None:
2954945eRobert Craigie1 years ago138response = await client.responses.create(
139model="gpt-4o", input="Explain disestablishmentarianism to a smart five year old."
08b8179aDavid Schnurr2 years ago140)
2954945eRobert Craigie1 years ago141print(response.output_text)
ede08829Jakub Roztocil3 years ago142
143
08b8179aDavid Schnurr2 years ago144asyncio.run(main())
2b21516eAtty Eleti3 years ago145```
ede08829Jakub Roztocil3 years ago146
08b8179aDavid Schnurr2 years ago147Functionality between the synchronous and asynchronous clients is otherwise identical.
148
c62e9907stainless-app[bot]1 years ago149### With aiohttp
150
151By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
152
153You can enable this by installing `aiohttp`:
154
155```sh
156# install from PyPI
157pip install openai[aiohttp]
158```
159
160Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
161
162```python
163import asyncio
164from openai import DefaultAioHttpClient
165from openai import AsyncOpenAI
166
167
168async def main() -> None:
169async with AsyncOpenAI(
2028ad2bstainless-app[bot]11 months ago170api_key="My API Key",
c62e9907stainless-app[bot]1 years ago171http_client=DefaultAioHttpClient(),
172) as client:
173chat_completion = await client.chat.completions.create(
174messages=[
175{
176"role": "user",
177"content": "Say this is a test",
178}
179],
180model="gpt-4o",
181)
182
183
184asyncio.run(main())
185```
186
2b23eb53Stainless Bot2 years ago187## Streaming responses
08b8179aDavid Schnurr2 years ago188
189We provide support for streaming responses using Server Side Events (SSE).
d53d9efbRachel Lim5 years ago190
08b8179aDavid Schnurr2 years ago191```python
192from openai import OpenAI
193
194client = OpenAI()
d53d9efbRachel Lim5 years ago195
2954945eRobert Craigie1 years ago196stream = client.responses.create(
23444ed9Stainless Bot1 years ago197model="gpt-4o",
2954945eRobert Craigie1 years ago198input="Write a one-sentence bedtime story about a unicorn.",
08b8179aDavid Schnurr2 years ago199stream=True,
200)
2954945eRobert Craigie1 years ago201
202for event in stream:
203print(event)
d53d9efbRachel Lim5 years ago204```
205
08b8179aDavid Schnurr2 years ago206The async client uses the exact same interface.
d53d9efbRachel Lim5 years ago207
208```python
db0aa22cAdel Basli1 years ago209import asyncio
08b8179aDavid Schnurr2 years ago210from openai import AsyncOpenAI
211
212client = AsyncOpenAI()
213
a3da0196Stainless Bot2 years ago214
dfe1c8daSahand Sojoodi2 years ago215async def main():
f588695fstainless-app[bot]1 years ago216stream = await client.responses.create(
2954945eRobert Craigie1 years ago217model="gpt-4o",
218input="Write a one-sentence bedtime story about a unicorn.",
dfe1c8daSahand Sojoodi2 years ago219stream=True,
220)
d53d9efbRachel Lim5 years ago221
f588695fstainless-app[bot]1 years ago222async for event in stream:
2954945eRobert Craigie1 years ago223print(event)
08b8179aDavid Schnurr2 years ago224
225
2954945eRobert Craigie1 years ago226asyncio.run(main())
62b73b9bAtty Eleti3 years ago227```
228
488ec04bRobert Craigie1 years ago229## Realtime API beta
230
231The Realtime API enables you to build low-latency, multi-modal conversational experiences. It currently supports text and audio as both input and output, as well as [function calling](https://platform.openai.com/docs/guides/function-calling) through a WebSocket connection.
232
233Under the hood the SDK uses the [`websockets`](https://websockets.readthedocs.io/en/stable/) library to manage connections.
234
255677d7Robert Craigie1 years ago235The Realtime API works through a combination of client-sent events and server-sent events. Clients can send events to do things like update session configuration or send text and audio inputs. Server events confirm when audio responses have completed, or when a text response from the model has been received. A full event reference can be found [here](https://platform.openai.com/docs/api-reference/realtime-client-events) and a guide can be found [here](https://platform.openai.com/docs/guides/realtime).
488ec04bRobert Craigie1 years ago236
237Basic text based example:
238
239```py
240import asyncio
241from openai import AsyncOpenAI
242
243async def main():
244client = AsyncOpenAI()
245
fd763424Robert Craigie1 years ago246async with client.beta.realtime.connect(model="gpt-4o-realtime-preview") as connection:
488ec04bRobert Craigie1 years ago247await connection.session.update(session={'modalities': ['text']})
248
249await connection.conversation.item.create(
250item={
251"type": "message",
252"role": "user",
253"content": [{"type": "input_text", "text": "Say hello!"}],
254}
255)
256await connection.response.create()
257
258async for event in connection:
259if event.type == 'response.text.delta':
260print(event.delta, flush=True, end="")
261
262elif event.type == 'response.text.done':
263print()
264
265elif event.type == "response.done":
266break
267
268asyncio.run(main())
269```
270
6935dfdcRobert Craigie1 years ago271However the real magic of the Realtime API is handling audio inputs / outputs, see this example [TUI script](https://github.com/openai/openai-python/blob/main/examples/realtime/push_to_talk_app.py) for a fully fledged example.
488ec04bRobert Craigie1 years ago272
273### Realtime error handling
274
2954945eRobert Craigie1 years ago275Whenever an error occurs, the Realtime API will send an [`error` event](https://platform.openai.com/docs/guides/realtime-model-capabilities#error-handling) and the connection will stay open and remain usable. This means you need to handle it yourself, as _no errors are raised directly_ by the SDK when an `error` event comes in.
488ec04bRobert Craigie1 years ago276
277```py
278client = AsyncOpenAI()
279
fd763424Robert Craigie1 years ago280async with client.beta.realtime.connect(model="gpt-4o-realtime-preview") as connection:
488ec04bRobert Craigie1 years ago281...
282async for event in connection:
283if event.type == 'error':
284print(event.error.type)
285print(event.error.code)
286print(event.error.event_id)
287print(event.error.message)
288```
289
08b8179aDavid Schnurr2 years ago290## Using types
291
47656567Stainless Bot2 years ago292Nested 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:
f4b9655fStainless Bot2 years ago293
47656567Stainless Bot2 years ago294- Serializing back into JSON, `model.to_json()`
295- Converting to a dictionary, `model.to_dict()`
2b21516eAtty Eleti3 years ago296
08b8179aDavid Schnurr2 years ago297Typed 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`.
0e21703eTed Sanders4 years ago298
08b8179aDavid Schnurr2 years ago299## Pagination
0e21703eTed Sanders4 years ago300
08b8179aDavid Schnurr2 years ago301List methods in the OpenAI API are paginated.
302
303This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually:
0e21703eTed Sanders4 years ago304
305```python
ff5add01stainless-app[bot]1 years ago306from openai import OpenAI
0e21703eTed Sanders4 years ago307
08b8179aDavid Schnurr2 years ago308client = OpenAI()
0e21703eTed Sanders4 years ago309
08b8179aDavid Schnurr2 years ago310all_jobs = []
311# Automatically fetches more pages as needed.
312for job in client.fine_tuning.jobs.list(
313limit=20,
314):
315# Do something with job here
316all_jobs.append(job)
317print(all_jobs)
0e21703eTed Sanders4 years ago318```
319
08b8179aDavid Schnurr2 years ago320Or, asynchronously:
0e21703eTed Sanders4 years ago321
08b8179aDavid Schnurr2 years ago322```python
323import asyncio
ff5add01stainless-app[bot]1 years ago324from openai import AsyncOpenAI
0e21703eTed Sanders4 years ago325
08b8179aDavid Schnurr2 years ago326client = AsyncOpenAI()
0e21703eTed Sanders4 years ago327
328
08b8179aDavid Schnurr2 years ago329async def main() -> None:
330all_jobs = []
331# Iterate through items across all pages, issuing requests as needed.
332async for job in client.fine_tuning.jobs.list(
333limit=20,
334):
335all_jobs.append(job)
336print(all_jobs)
0e21703eTed Sanders4 years ago337
62b51ca0Boris Dayma4 years ago338
08b8179aDavid Schnurr2 years ago339asyncio.run(main())
340```
2942bf4bLogan Kilpatrick2 years ago341
08b8179aDavid Schnurr2 years ago342Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages:
2942bf4bLogan Kilpatrick2 years ago343
08b8179aDavid Schnurr2 years ago344```python
345first_page = await client.fine_tuning.jobs.list(
346limit=20,
347)
348if first_page.has_next_page():
349print(f"will fetch next page using these details: {first_page.next_page_info()}")
350next_page = await first_page.get_next_page()
351print(f"number of items we just fetched: {len(next_page.data)}")
352
353# Remove `await` for non-async usage.
62b51ca0Boris Dayma4 years ago354```
355
08b8179aDavid Schnurr2 years ago356Or just work directly with the returned data:
0e21703eTed Sanders4 years ago357
08b8179aDavid Schnurr2 years ago358```python
359first_page = await client.fine_tuning.jobs.list(
360limit=20,
361)
e389823bMorgan McGuire2 years ago362
08b8179aDavid Schnurr2 years ago363print(f"next page cursor: {first_page.after}") # => "next page cursor: ..."
364for job in first_page.data:
365print(job.id)
e389823bMorgan McGuire2 years ago366
08b8179aDavid Schnurr2 years ago367# Remove `await` for non-async usage.
368```
e389823bMorgan McGuire2 years ago369
08b8179aDavid Schnurr2 years ago370## Nested params
3c00e856hallacy3 years ago371
08b8179aDavid Schnurr2 years ago372Nested parameters are dictionaries, typed using `TypedDict`, for example:
3c00e856hallacy3 years ago373
374```python
08b8179aDavid Schnurr2 years ago375from openai import OpenAI
3c00e856hallacy3 years ago376
08b8179aDavid Schnurr2 years ago377client = OpenAI()
378
2954945eRobert Craigie1 years ago379response = client.chat.responses.create(
380input=[
aa681899Stainless Bot2 years ago381{
382"role": "user",
2954945eRobert Craigie1 years ago383"content": "How much ?",
aa681899Stainless Bot2 years ago384}
385],
23444ed9Stainless Bot1 years ago386model="gpt-4o",
aa681899Stainless Bot2 years ago387response_format={"type": "json_object"},
388)
08b8179aDavid Schnurr2 years ago389```
3c00e856hallacy3 years ago390
2b23eb53Stainless Bot2 years ago391## File uploads
dc33cb9dMichelle Pokrass3 years ago392
acf68ef3stainless-app[bot]1 years ago393Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`.
dc33cb9dMichelle Pokrass3 years ago394
376dd199Logan Kilpatrick2 years ago395```python
08b8179aDavid Schnurr2 years ago396from pathlib import Path
397from openai import OpenAI
398
399client = OpenAI()
400
401client.files.create(
402file=Path("input.jsonl"),
403purpose="fine-tune",
404)
dc33cb9dMichelle Pokrass3 years ago405```
406
08b8179aDavid Schnurr2 years ago407The 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.
376dd199Logan Kilpatrick2 years ago408
18e0b36astainless-app[bot]1 years ago409## Webhook Verification
410
411Verifying webhook signatures is _optional but encouraged_.
412
4f99c4e6David Meadows1 years ago413For more information about webhooks, see [the API docs](https://platform.openai.com/docs/guides/webhooks).
414
18e0b36astainless-app[bot]1 years ago415### Parsing webhook payloads
416
417For most use cases, you will likely want to verify the webhook and parse the payload at the same time. To achieve this, we provide the method `client.webhooks.unwrap()`, which parses a webhook request and verifies that it was sent by OpenAI. This method will raise an error if the signature is invalid.
418
419Note that the `body` parameter must be the raw JSON string sent from the server (do not parse it first). The `.unwrap()` method will parse this JSON for you into an event object after verifying the webhook was sent from OpenAI.
420
421```python
422from openai import OpenAI
423from flask import Flask, request
424
425app = Flask(__name__)
426client = OpenAI() # OPENAI_WEBHOOK_SECRET environment variable is used by default
427
428
429@app.route("/webhook", methods=["POST"])
430def webhook():
431request_body = request.get_data(as_text=True)
432
433try:
434event = client.webhooks.unwrap(request_body, request.headers)
435
436if event.type == "response.completed":
437print("Response completed:", event.data)
438elif event.type == "response.failed":
439print("Response failed:", event.data)
440else:
441print("Unhandled event type:", event.type)
442
443return "ok"
444except Exception as e:
445print("Invalid signature:", e)
446return "Invalid signature", 400
447
448
449if __name__ == "__main__":
450app.run(port=8000)
451```
452
453### Verifying webhook payloads directly
454
455In some cases, you may want to verify the webhook separately from parsing the payload. If you prefer to handle these steps separately, we provide the method `client.webhooks.verify_signature()` to _only verify_ the signature of a webhook request. Like `.unwrap()`, this method will raise an error if the signature is invalid.
456
457Note that the `body` parameter must be the raw JSON string sent from the server (do not parse it first). You will then need to parse the body after verifying the signature.
458
459```python
460import json
461from openai import OpenAI
462from flask import Flask, request
463
464app = Flask(__name__)
465client = OpenAI() # OPENAI_WEBHOOK_SECRET environment variable is used by default
466
467
468@app.route("/webhook", methods=["POST"])
469def webhook():
470request_body = request.get_data(as_text=True)
471
472try:
473client.webhooks.verify_signature(request_body, request.headers)
474
475# Parse the body after verification
476event = json.loads(request_body)
477print("Verified event:", event)
478
479return "ok"
480except Exception as e:
481print("Invalid signature:", e)
482return "Invalid signature", 400
483
484
485if __name__ == "__main__":
486app.run(port=8000)
487```
488
08b8179aDavid Schnurr2 years ago489## Handling errors
376dd199Logan Kilpatrick2 years ago490
08b8179aDavid Schnurr2 years ago491When 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.
2b21516eAtty Eleti3 years ago492
08b8179aDavid Schnurr2 years ago493When the API returns a non-success status code (that is, 4xx or 5xx
494response), a subclass of `openai.APIStatusError` is raised, containing `status_code` and `response` properties.
62b73b9bAtty Eleti3 years ago495
08b8179aDavid Schnurr2 years ago496All errors inherit from `openai.APIError`.
497
498```python
499import openai
500from openai import OpenAI
501
502client = OpenAI()
503
504try:
8dab1421Stainless Bot2 years ago505client.fine_tuning.jobs.create(
23444ed9Stainless Bot1 years ago506model="gpt-4o",
8dab1421Stainless Bot2 years ago507training_file="file-abc123",
08b8179aDavid Schnurr2 years ago508)
509except openai.APIConnectionError as e:
510print("The server could not be reached")
511print(e.__cause__) # an underlying Exception, likely raised within httpx.
512except openai.RateLimitError as e:
513print("A 429 status code was received; we should back off a bit.")
514except openai.APIStatusError as e:
515print("Another non-200-range status code was received")
516print(e.status_code)
517print(e.response)
62b73b9bAtty Eleti3 years ago518```
519
fee9c81bstainless-app[bot]1 years ago520Error codes are as follows:
08b8179aDavid Schnurr2 years ago521
522| Status Code | Error Type |
523| ----------- | -------------------------- |
524| 400 | `BadRequestError` |
525| 401 | `AuthenticationError` |
526| 403 | `PermissionDeniedError` |
527| 404 | `NotFoundError` |
528| 422 | `UnprocessableEntityError` |
529| 429 | `RateLimitError` |
530| >=500 | `InternalServerError` |
531| N/A | `APIConnectionError` |
532
4b302346Robert Craigie1 years ago533## Request IDs
534
535> For more information on debugging requests, see [these docs](https://platform.openai.com/docs/api-reference/debugging-requests)
536
537All 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.
538
539```python
2954945eRobert Craigie1 years ago540response = await client.responses.create(
541model="gpt-4o-mini",
542input="Say 'this is a test'.",
4b302346Robert Craigie1 years ago543)
2954945eRobert Craigie1 years ago544print(response._request_id) # req_123
4b302346Robert Craigie1 years ago545```
546
547Note that unlike other properties that use an `_` prefix, the `_request_id` property
2954945eRobert Craigie1 years ago548_is_ public. Unless documented otherwise, _all_ other `_` prefix properties,
549methods and modules are _private_.
4b302346Robert Craigie1 years ago550
709926ffRobert Craigie1 years ago551> [!IMPORTANT]
552> If you need to access request IDs for failed requests you must catch the `APIStatusError` exception
553
554```python
555import openai
556
557try:
558completion = await client.chat.completions.create(
559messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-4"
560)
561except openai.APIStatusError as exc:
562print(exc.request_id) # req_123
563raise exc
564```
565
2954945eRobert Craigie1 years ago566## Retries
376dd199Logan Kilpatrick2 years ago567
08b8179aDavid Schnurr2 years ago568Certain errors are automatically retried 2 times by default, with a short exponential backoff.
569Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,
570429 Rate Limit, and >=500 Internal errors are all retried by default.
0abf6413Andrew Chen Wang3 years ago571
08b8179aDavid Schnurr2 years ago572You can use the `max_retries` option to configure or disable retry settings:
0abf6413Andrew Chen Wang3 years ago573
574```python
08b8179aDavid Schnurr2 years ago575from openai import OpenAI
576
577# Configure the default for all requests:
578client = OpenAI(
579# default is 2
580max_retries=0,
581)
582
583# Or, configure per-request:
584client.with_options(max_retries=5).chat.completions.create(
585messages=[
586{
587"role": "user",
23444ed9Stainless Bot1 years ago588"content": "How can I get the name of the current day in JavaScript?",
08b8179aDavid Schnurr2 years ago589}
590],
23444ed9Stainless Bot1 years ago591model="gpt-4o",
08b8179aDavid Schnurr2 years ago592)
0abf6413Andrew Chen Wang3 years ago593```
594
2954945eRobert Craigie1 years ago595## Timeouts
0abf6413Andrew Chen Wang3 years ago596
08b8179aDavid Schnurr2 years ago597By default requests time out after 10 minutes. You can configure this with a `timeout` option,
90e3d396Guspan Tanadi1 years ago598which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:
376dd199Logan Kilpatrick2 years ago599
08b8179aDavid Schnurr2 years ago600```python
601from openai import OpenAI
602
603# Configure the default for all requests:
604client = OpenAI(
1381f46eStainless Bot2 years ago605# 20 seconds (default is 10 minutes)
08b8179aDavid Schnurr2 years ago606timeout=20.0,
607)
608
609# More granular control:
610client = OpenAI(
611timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
612)
613
614# Override per-request:
2a678e30Stainless Bot2 years ago615client.with_options(timeout=5.0).chat.completions.create(
08b8179aDavid Schnurr2 years ago616messages=[
617{
618"role": "user",
619"content": "How can I list all files in a directory using Python?",
620}
621],
23444ed9Stainless Bot1 years ago622model="gpt-4o",
08b8179aDavid Schnurr2 years ago623)
0abf6413Andrew Chen Wang3 years ago624```
625
08b8179aDavid Schnurr2 years ago626On timeout, an `APITimeoutError` is thrown.
376dd199Logan Kilpatrick2 years ago627
08b8179aDavid Schnurr2 years ago628Note that requests that time out are [retried twice by default](#retries).
376dd199Logan Kilpatrick2 years ago629
08b8179aDavid Schnurr2 years ago630## Advanced
376dd199Logan Kilpatrick2 years ago631
08b8179aDavid Schnurr2 years ago632### Logging
376dd199Logan Kilpatrick2 years ago633
08b8179aDavid Schnurr2 years ago634We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.
376dd199Logan Kilpatrick2 years ago635
f6199d60stainless-app[bot]1 years ago636You can enable logging by setting the environment variable `OPENAI_LOG` to `info`.
376dd199Logan Kilpatrick2 years ago637
08b8179aDavid Schnurr2 years ago638```shell
f6199d60stainless-app[bot]1 years ago639$ export OPENAI_LOG=info
376dd199Logan Kilpatrick2 years ago640```
641
f6199d60stainless-app[bot]1 years ago642Or to `debug` for more verbose logging.
643
08b8179aDavid Schnurr2 years ago644### How to tell whether `None` means `null` or missing
dc33cb9dMichelle Pokrass3 years ago645
08b8179aDavid Schnurr2 years ago646In 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`:
3c6d4cd6Greg Brockman5 years ago647
08b8179aDavid Schnurr2 years ago648```py
649if response.my_field is None:
650if 'my_field' not in response.model_fields_set:
651print('Got json like {}, without a "my_field" key present at all.')
652else:
653print('Got json like {"my_field": null}.')
654```
376dd199Logan Kilpatrick2 years ago655
08b8179aDavid Schnurr2 years ago656### Accessing raw response data (e.g. headers)
376dd199Logan Kilpatrick2 years ago657
86379b44Stainless Bot2 years ago658The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g.,
08b8179aDavid Schnurr2 years ago659
660```py
661from openai import OpenAI
662
663client = OpenAI()
664response = client.chat.completions.with_raw_response.create(
665messages=[{
666"role": "user",
667"content": "Say this is a test",
668}],
23444ed9Stainless Bot1 years ago669model="gpt-4o",
08b8179aDavid Schnurr2 years ago670)
671print(response.headers.get('X-My-Header'))
672
673completion = response.parse() # get the object that `chat.completions.create()` would have returned
674print(completion)
376dd199Logan Kilpatrick2 years ago675```
676
16315f22stainless-app[bot]1 years ago677These methods return a [`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.
86379b44Stainless Bot2 years ago678
679For the sync client this will mostly be the same with the exception
680of `content` & `text` will be methods instead of properties. In the
681async client, all methods will be async.
682
683A migration script will be provided & the migration in general should
684be smooth.
685
686#### `.with_streaming_response`
687
688The above interface eagerly reads the full response body when you make the request, which may not always be what you want.
689
690To 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.
691
692As 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.
693
694```python
695with client.chat.completions.with_streaming_response.create(
696messages=[
697{
698"role": "user",
699"content": "Say this is a test",
700}
701],
23444ed9Stainless Bot1 years ago702model="gpt-4o",
86379b44Stainless Bot2 years ago703) as response:
704print(response.headers.get("X-My-Header"))
705
706for line in response.iter_lines():
707print(line)
708```
709
710The context manager is required so that the response will reliably be closed.
3c6d4cd6Greg Brockman5 years ago711
73869eeeStainless Bot2 years ago712### Making custom/undocumented requests
713
7931ebaaStainless Bot2 years ago714This library is typed for convenient access to the documented API.
73869eeeStainless Bot2 years ago715
716If you need to access undocumented endpoints, params, or response properties, the library can still be used.
717
718#### Undocumented endpoints
719
720To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other
fee9c81bstainless-app[bot]1 years ago721http verbs. Options on the client will be respected (such as retries) when making this request.
73869eeeStainless Bot2 years ago722
723```py
724import httpx
725
726response = client.post(
727"/foo",
728cast_to=httpx.Response,
729body={"my_param": True},
730)
731
732print(response.headers.get("x-foo"))
733```
734
802819c8Stainless Bot2 years ago735#### Undocumented request params
73869eeeStainless Bot2 years ago736
737If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request
738options.
739
802819c8Stainless Bot2 years ago740#### Undocumented response properties
73869eeeStainless Bot2 years ago741
742To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You
743can also get all the extra fields on the Pydantic model as a dict with
744[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra).
745
08b8179aDavid Schnurr2 years ago746### Configuring the HTTP client
376dd199Logan Kilpatrick2 years ago747
08b8179aDavid Schnurr2 years ago748You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including:
376dd199Logan Kilpatrick2 years ago749
6a1ab551stainless-app[bot]1 years ago750- Support for [proxies](https://www.python-httpx.org/advanced/proxies/)
751- Custom [transports](https://www.python-httpx.org/advanced/transports/)
d3254d12stainless-app[bot]2 years ago752- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality
376dd199Logan Kilpatrick2 years ago753
754```python
6a1ab551stainless-app[bot]1 years ago755import httpx
347363edStainless Bot2 years ago756from openai import OpenAI, DefaultHttpxClient
08b8179aDavid Schnurr2 years ago757
758client = OpenAI(
0733934fStainless Bot2 years ago759# Or use the `OPENAI_BASE_URL` env var
38dd5348Adrian Cole1 years ago760base_url="http://my.test.server.example.com:8083/v1",
347363edStainless Bot2 years ago761http_client=DefaultHttpxClient(
6a1ab551stainless-app[bot]1 years ago762proxy="http://my.test.proxy.example.com",
08b8179aDavid Schnurr2 years ago763transport=httpx.HTTPTransport(local_address="0.0.0.0"),
764),
765)
766```
767
f8f01a61stainless-app[bot]1 years ago768You can also customize the client on a per-request basis by using `with_options()`:
769
770```python
771client.with_options(http_client=DefaultHttpxClient(...))
772```
773
08b8179aDavid Schnurr2 years ago774### Managing HTTP resources
775
776By 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.
777
588935e2stainless-app[bot]1 years ago778```py
779from openai import OpenAI
780
781with OpenAI() as client:
782# make requests here
783...
784
785# HTTP client is now closed
786```
787
08b8179aDavid Schnurr2 years ago788## Microsoft Azure OpenAI
789
6e7d8548Scott Addie2 years ago790To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the `AzureOpenAI`
08b8179aDavid Schnurr2 years ago791class instead of the `OpenAI` class.
792
793> [!IMPORTANT]
794> The Azure API shape differs from the core API shape which means that the static types for responses / params
795> won't always be correct.
376dd199Logan Kilpatrick2 years ago796
08b8179aDavid Schnurr2 years ago797```py
798from openai import AzureOpenAI
376dd199Logan Kilpatrick2 years ago799
08b8179aDavid Schnurr2 years ago800# gets the API Key from environment variable AZURE_OPENAI_API_KEY
801client = AzureOpenAI(
6e7d8548Scott Addie2 years ago802# https://learn.microsoft.com/azure/ai-services/openai/reference#rest-api-versioning
819ae68dJackYu2 years ago803api_version="2023-07-01-preview",
6e7d8548Scott Addie2 years ago804# https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource
08b8179aDavid Schnurr2 years ago805azure_endpoint="https://example-endpoint.openai.azure.com",
806)
807
808completion = client.chat.completions.create(
809model="deployment-name", # e.g. gpt-35-instant
810messages=[
811{
812"role": "user",
813"content": "How do I output all files in a directory using Python?",
814},
815],
816)
47656567Stainless Bot2 years ago817print(completion.to_json())
376dd199Logan Kilpatrick2 years ago818```
3c6d4cd6Greg Brockman5 years ago819
08b8179aDavid Schnurr2 years ago820In addition to the options provided in the base `OpenAI` client, the following options are provided:
821
7758c54bStainless Bot2 years ago822- `azure_endpoint` (or the `AZURE_OPENAI_ENDPOINT` environment variable)
08b8179aDavid Schnurr2 years ago823- `azure_deployment`
7758c54bStainless Bot2 years ago824- `api_version` (or the `OPENAI_API_VERSION` environment variable)
825- `azure_ad_token` (or the `AZURE_OPENAI_AD_TOKEN` environment variable)
08b8179aDavid Schnurr2 years ago826- `azure_ad_token_provider`
827
6e7d8548Scott Addie2 years ago828An 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).
08b8179aDavid Schnurr2 years ago829
830## Versioning
831
832This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
833
8341. Changes that only affect static types, without breaking runtime behavior.
e502d301Josiah Altschuler1 years ago8352. 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.)_
08b8179aDavid Schnurr2 years ago8363. Changes that we do not expect to impact the vast majority of users in practice.
837
838We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
839
840We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-python/issues) with questions, bugs, or suggestions.
841
fee10404stainless-app[bot]1 years ago842### Determining the installed version
843
844If 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.
845
846You can determine the version that is being used at runtime with:
847
848```py
849import openai
850print(openai.__version__)
851```
852
08b8179aDavid Schnurr2 years ago853## Requirements
3c6d4cd6Greg Brockman5 years ago854
cb88c2f0stainless-app[bot]1 years ago855Python 3.8 or higher.
a3001d8dStainless Bot1 years ago856
857## Contributing
858
859See [the contributing documentation](./CONTRIBUTING.md).