openai/openai-python

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
ba421a4af8936e55ee3a4287ace91b0776c8ccba

Branches

Tags

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

Clone

HTTPS

Download ZIP

README.md

577lines · modecode

1# OpenAI Python API library
2
3<!-- prettier-ignore -->
4[![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/)
5
6The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.9+
7application. 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
10## Documentation
11
12The 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).
13
14## Installation
15
16```sh
17# install from PyPI
18pip install openai
19```
20
21## Usage
22
23The full API of this library can be found in [api.md](api.md).
24
25```python
26import os
27from openai import OpenAI
28
29client = OpenAI(
30 api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted
31)
32
33chat_completion = client.chat.completions.create(
34 messages=[
35 {
36 "role": "user",
37 "content": "Say this is a test",
38 }
39 ],
40 model="gpt-4o",
41)
42```
43
44While you can provide an `api_key` keyword argument,
45we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)
46to add `OPENAI_API_KEY="My API Key"` to your `.env` file
47so that your API Key is not stored in source control.
48
49## Async usage
50
51Simply import `AsyncOpenAI` instead of `OpenAI` and use `await` with each API call:
52
53```python
54import os
55import asyncio
56from openai import AsyncOpenAI
57
58client = AsyncOpenAI(
59 api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted
60)
61
62
63async def main() -> None:
64 chat_completion = await client.chat.completions.create(
65 messages=[
66 {
67 "role": "user",
68 "content": "Say this is a test",
69 }
70 ],
71 model="gpt-4o",
72 )
73
74
75asyncio.run(main())
76```
77
78Functionality between the synchronous and asynchronous clients is otherwise identical.
79
80### With aiohttp
81
82By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
83
84You can enable this by installing `aiohttp`:
85
86```sh
87# install from PyPI
88pip install openai[aiohttp]
89```
90
91Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
92
93```python
94import os
95import asyncio
96from openai import DefaultAioHttpClient
97from openai import AsyncOpenAI
98
99
100async def main() -> None:
101 async with AsyncOpenAI(
102 api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted
103 http_client=DefaultAioHttpClient(),
104 ) as client:
105 chat_completion = await client.chat.completions.create(
106 messages=[
107 {
108 "role": "user",
109 "content": "Say this is a test",
110 }
111 ],
112 model="gpt-4o",
113 )
114
115
116asyncio.run(main())
117```
118
119## Streaming responses
120
121We provide support for streaming responses using Server Side Events (SSE).
122
123```python
124from openai import OpenAI
125
126client = OpenAI()
127
128stream = client.chat.completions.create(
129 messages=[
130 {
131 "role": "user",
132 "content": "Say this is a test",
133 }
134 ],
135 model="gpt-4o",
136 stream=True,
137)
138for chat_completion in stream:
139 print(chat_completion)
140```
141
142The async client uses the exact same interface.
143
144```python
145from openai import AsyncOpenAI
146
147client = AsyncOpenAI()
148
149stream = await client.chat.completions.create(
150 messages=[
151 {
152 "role": "user",
153 "content": "Say this is a test",
154 }
155 ],
156 model="gpt-4o",
157 stream=True,
158)
159async for chat_completion in stream:
160 print(chat_completion)
161```
162
163## Using types
164
165Nested 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:
166
167- Serializing back into JSON, `model.to_json()`
168- Converting to a dictionary, `model.to_dict()`
169
170Typed 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`.
171
172## Pagination
173
174List methods in the OpenAI API are paginated.
175
176This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually:
177
178```python
179from openai import OpenAI
180
181client = OpenAI()
182
183all_jobs = []
184# Automatically fetches more pages as needed.
185for job in client.fine_tuning.jobs.list(
186 limit=20,
187):
188 # Do something with job here
189 all_jobs.append(job)
190print(all_jobs)
191```
192
193Or, asynchronously:
194
195```python
196import asyncio
197from openai import AsyncOpenAI
198
199client = AsyncOpenAI()
200
201
202async def main() -> None:
203 all_jobs = []
204 # Iterate through items across all pages, issuing requests as needed.
205 async for job in client.fine_tuning.jobs.list(
206 limit=20,
207 ):
208 all_jobs.append(job)
209 print(all_jobs)
210
211
212asyncio.run(main())
213```
214
215Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages:
216
217```python
218first_page = await client.fine_tuning.jobs.list(
219 limit=20,
220)
221if first_page.has_next_page():
222 print(f"will fetch next page using these details: {first_page.next_page_info()}")
223 next_page = await first_page.get_next_page()
224 print(f"number of items we just fetched: {len(next_page.data)}")
225
226# Remove `await` for non-async usage.
227```
228
229Or just work directly with the returned data:
230
231```python
232first_page = await client.fine_tuning.jobs.list(
233 limit=20,
234)
235
236print(f"next page cursor: {first_page.after}") # => "next page cursor: ..."
237for job in first_page.data:
238 print(job.id)
239
240# Remove `await` for non-async usage.
241```
242
243## Nested params
244
245Nested parameters are dictionaries, typed using `TypedDict`, for example:
246
247```python
248from openai import OpenAI
249
250client = OpenAI()
251
252completion = client.chat.completions.create(
253 messages=[
254 {
255 "role": "user",
256 "content": "Can you generate an example json object describing a fruit?",
257 }
258 ],
259 model="gpt-4o",
260 response_format={"type": "json_object"},
261)
262```
263
264## File uploads
265
266Request 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)`.
267
268```python
269from pathlib import Path
270from openai import OpenAI
271
272client = OpenAI()
273
274client.files.create(
275 file=Path("input.jsonl"),
276 purpose="fine-tune",
277)
278```
279
280The 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.
281
282## Handling errors
283
284When 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.
285
286When the API returns a non-success status code (that is, 4xx or 5xx
287response), a subclass of `openai.APIStatusError` is raised, containing `status_code` and `response` properties.
288
289All errors inherit from `openai.APIError`.
290
291```python
292import openai
293from openai import OpenAI
294
295client = OpenAI()
296
297try:
298 client.fine_tuning.jobs.create(
299 model="gpt-4o",
300 training_file="file-abc123",
301 )
302except openai.APIConnectionError as e:
303 print("The server could not be reached")
304 print(e.__cause__) # an underlying Exception, likely raised within httpx.
305except openai.RateLimitError as e:
306 print("A 429 status code was received; we should back off a bit.")
307except openai.APIStatusError as e:
308 print("Another non-200-range status code was received")
309 print(e.status_code)
310 print(e.response)
311```
312
313Error codes are as follows:
314
315| Status Code | Error Type |
316| ----------- | -------------------------- |
317| 400 | `BadRequestError` |
318| 401 | `AuthenticationError` |
319| 403 | `PermissionDeniedError` |
320| 404 | `NotFoundError` |
321| 422 | `UnprocessableEntityError` |
322| 429 | `RateLimitError` |
323| >=500 | `InternalServerError` |
324| N/A | `APIConnectionError` |
325
326### Retries
327
328Certain errors are automatically retried 2 times by default, with a short exponential backoff.
329Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,
330429 Rate Limit, and >=500 Internal errors are all retried by default.
331
332You can use the `max_retries` option to configure or disable retry settings:
333
334```python
335from openai import OpenAI
336
337# Configure the default for all requests:
338client = OpenAI(
339 # default is 2
340 max_retries=0,
341)
342
343# Or, configure per-request:
344client.with_options(max_retries=5).chat.completions.create(
345 messages=[
346 {
347 "role": "user",
348 "content": "How can I get the name of the current day in JavaScript?",
349 }
350 ],
351 model="gpt-4o",
352)
353```
354
355### Timeouts
356
357By default requests time out after 10 minutes. You can configure this with a `timeout` option,
358which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:
359
360```python
361from openai import OpenAI
362
363# Configure the default for all requests:
364client = OpenAI(
365 # 20 seconds (default is 10 minutes)
366 timeout=20.0,
367)
368
369# More granular control:
370client = OpenAI(
371 timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
372)
373
374# Override per-request:
375client.with_options(timeout=5.0).chat.completions.create(
376 messages=[
377 {
378 "role": "user",
379 "content": "How can I list all files in a directory using Python?",
380 }
381 ],
382 model="gpt-4o",
383)
384```
385
386On timeout, an `APITimeoutError` is thrown.
387
388Note that requests that time out are [retried twice by default](#retries).
389
390## Advanced
391
392### Logging
393
394We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.
395
396You can enable logging by setting the environment variable `OPENAI_LOG` to `info`.
397
398```shell
399$ export OPENAI_LOG=info
400```
401
402Or to `debug` for more verbose logging.
403
404### How to tell whether `None` means `null` or missing
405
406In 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`:
407
408```py
409if response.my_field is None:
410 if 'my_field' not in response.model_fields_set:
411 print('Got json like {}, without a "my_field" key present at all.')
412 else:
413 print('Got json like {"my_field": null}.')
414```
415
416### Accessing raw response data (e.g. headers)
417
418The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g.,
419
420```py
421from openai import OpenAI
422
423client = OpenAI()
424response = client.chat.completions.with_raw_response.create(
425 messages=[{
426 "role": "user",
427 "content": "Say this is a test",
428 }],
429 model="gpt-4o",
430)
431print(response.headers.get('X-My-Header'))
432
433completion = response.parse() # get the object that `chat.completions.create()` would have returned
434print(completion)
435```
436
437These 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.
438
439For the sync client this will mostly be the same with the exception
440of `content` & `text` will be methods instead of properties. In the
441async client, all methods will be async.
442
443A migration script will be provided & the migration in general should
444be smooth.
445
446#### `.with_streaming_response`
447
448The above interface eagerly reads the full response body when you make the request, which may not always be what you want.
449
450To 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.
451
452As 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.
453
454```python
455with client.chat.completions.with_streaming_response.create(
456 messages=[
457 {
458 "role": "user",
459 "content": "Say this is a test",
460 }
461 ],
462 model="gpt-4o",
463) as response:
464 print(response.headers.get("X-My-Header"))
465
466 for line in response.iter_lines():
467 print(line)
468```
469
470The context manager is required so that the response will reliably be closed.
471
472### Making custom/undocumented requests
473
474This library is typed for convenient access to the documented API.
475
476If you need to access undocumented endpoints, params, or response properties, the library can still be used.
477
478#### Undocumented endpoints
479
480To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other
481http verbs. Options on the client will be respected (such as retries) when making this request.
482
483```py
484import httpx
485
486response = client.post(
487 "/foo",
488 cast_to=httpx.Response,
489 body={"my_param": True},
490)
491
492print(response.headers.get("x-foo"))
493```
494
495#### Undocumented request params
496
497If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request
498options.
499
500#### Undocumented response properties
501
502To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You
503can also get all the extra fields on the Pydantic model as a dict with
504[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra).
505
506### Configuring the HTTP client
507
508You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including:
509
510- Support for [proxies](https://www.python-httpx.org/advanced/proxies/)
511- Custom [transports](https://www.python-httpx.org/advanced/transports/)
512- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality
513
514```python
515import httpx
516from openai import OpenAI, DefaultHttpxClient
517
518client = OpenAI(
519 # Or use the `OPENAI_BASE_URL` env var
520 base_url="http://my.test.server.example.com:8083",
521 http_client=DefaultHttpxClient(
522 proxy="http://my.test.proxy.example.com",
523 transport=httpx.HTTPTransport(local_address="0.0.0.0"),
524 ),
525)
526```
527
528You can also customize the client on a per-request basis by using `with_options()`:
529
530```python
531client.with_options(http_client=DefaultHttpxClient(...))
532```
533
534### Managing HTTP resources
535
536By 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.
537
538```py
539from openai import OpenAI
540
541with OpenAI() as client:
542 # make requests here
543 ...
544
545# HTTP client is now closed
546```
547
548## Versioning
549
550This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
551
5521. Changes that only affect static types, without breaking runtime behavior.
5532. 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.)_
5543. Changes that we do not expect to impact the vast majority of users in practice.
555
556We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
557
558We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-python/issues) with questions, bugs, or suggestions.
559
560### Determining the installed version
561
562If 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.
563
564You can determine the version that is being used at runtime with:
565
566```py
567import openai
568print(openai.__version__)
569```
570
571## Requirements
572
573Python 3.9 or higher.
574
575## Contributing
576
577See [the contributing documentation](./CONTRIBUTING.md).