openai/openai-python

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
26d1a93458f754e6b8d5d1b2507e208393f06bda

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/openai/_client.py

488lines · modecode

1# File generated from our OpenAPI spec by Stainless.
2
3from __future__ import annotations
4
5import os
6import asyncio
7from typing import Union, Mapping
8from typing_extensions import override
9
10import httpx
11
12from . import resources, _exceptions
13from ._qs import Querystring
14from ._types import (
15 NOT_GIVEN,
16 Omit,
17 Timeout,
18 NotGiven,
19 Transport,
20 ProxiesTypes,
21 RequestOptions,
22)
23from ._utils import is_given
24from ._version import __version__
25from ._streaming import Stream as Stream
26from ._streaming import AsyncStream as AsyncStream
27from ._exceptions import OpenAIError, APIStatusError
28from ._base_client import DEFAULT_MAX_RETRIES, SyncAPIClient, AsyncAPIClient
29
30__all__ = [
31 "Timeout",
32 "Transport",
33 "ProxiesTypes",
34 "RequestOptions",
35 "resources",
36 "OpenAI",
37 "AsyncOpenAI",
38 "Client",
39 "AsyncClient",
40]
41
42
43class OpenAI(SyncAPIClient):
44 completions: resources.Completions
45 chat: resources.Chat
46 edits: resources.Edits
47 embeddings: resources.Embeddings
48 files: resources.Files
49 images: resources.Images
50 audio: resources.Audio
51 moderations: resources.Moderations
52 models: resources.Models
53 fine_tuning: resources.FineTuning
54 fine_tunes: resources.FineTunes
55 with_raw_response: OpenAIWithRawResponse
56
57 # client options
58 api_key: str
59 organization: str | None
60
61 def __init__(
62 self,
63 *,
64 api_key: str | None = None,
65 organization: str | None = None,
66 base_url: str | httpx.URL | None = None,
67 timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
68 max_retries: int = DEFAULT_MAX_RETRIES,
69 default_headers: Mapping[str, str] | None = None,
70 default_query: Mapping[str, object] | None = None,
71 # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details.
72 http_client: httpx.Client | None = None,
73 # Enable or disable schema validation for data returned by the API.
74 # When enabled an error APIResponseValidationError is raised
75 # if the API responds with invalid data for the expected schema.
76 #
77 # This parameter may be removed or changed in the future.
78 # If you rely on this feature, please open a GitHub issue
79 # outlining your use-case to help us decide if it should be
80 # part of our public interface in the future.
81 _strict_response_validation: bool = False,
82 ) -> None:
83 """Construct a new synchronous openai client instance.
84
85 This automatically infers the following arguments from their corresponding environment variables if they are not provided:
86 - `api_key` from `OPENAI_API_KEY`
87 - `organization` from `OPENAI_ORG_ID`
88 """
89 if api_key is None:
90 api_key = os.environ.get("OPENAI_API_KEY")
91 if api_key is None:
92 raise OpenAIError(
93 "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable"
94 )
95 self.api_key = api_key
96
97 if organization is None:
98 organization = os.environ.get("OPENAI_ORG_ID")
99 self.organization = organization
100
101 if base_url is None:
102 base_url = f"https://api.openai.com/v1"
103
104 super().__init__(
105 version=__version__,
106 base_url=base_url,
107 max_retries=max_retries,
108 timeout=timeout,
109 http_client=http_client,
110 custom_headers=default_headers,
111 custom_query=default_query,
112 _strict_response_validation=_strict_response_validation,
113 )
114
115 self._default_stream_cls = Stream
116
117 self.completions = resources.Completions(self)
118 self.chat = resources.Chat(self)
119 self.edits = resources.Edits(self)
120 self.embeddings = resources.Embeddings(self)
121 self.files = resources.Files(self)
122 self.images = resources.Images(self)
123 self.audio = resources.Audio(self)
124 self.moderations = resources.Moderations(self)
125 self.models = resources.Models(self)
126 self.fine_tuning = resources.FineTuning(self)
127 self.fine_tunes = resources.FineTunes(self)
128 self.with_raw_response = OpenAIWithRawResponse(self)
129
130 @property
131 @override
132 def qs(self) -> Querystring:
133 return Querystring(array_format="comma")
134
135 @property
136 @override
137 def auth_headers(self) -> dict[str, str]:
138 api_key = self.api_key
139 return {"Authorization": f"Bearer {api_key}"}
140
141 @property
142 @override
143 def default_headers(self) -> dict[str, str | Omit]:
144 return {
145 **super().default_headers,
146 "OpenAI-Organization": self.organization if self.organization is not None else Omit(),
147 **self._custom_headers,
148 }
149
150 def copy(
151 self,
152 *,
153 api_key: str | None = None,
154 organization: str | None = None,
155 base_url: str | httpx.URL | None = None,
156 timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
157 http_client: httpx.Client | None = None,
158 max_retries: int | NotGiven = NOT_GIVEN,
159 default_headers: Mapping[str, str] | None = None,
160 set_default_headers: Mapping[str, str] | None = None,
161 default_query: Mapping[str, object] | None = None,
162 set_default_query: Mapping[str, object] | None = None,
163 ) -> OpenAI:
164 """
165 Create a new client instance re-using the same options given to the current client with optional overriding.
166
167 It should be noted that this does not share the underlying httpx client class which may lead
168 to performance issues.
169 """
170 if default_headers is not None and set_default_headers is not None:
171 raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")
172
173 if default_query is not None and set_default_query is not None:
174 raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive")
175
176 headers = self._custom_headers
177 if default_headers is not None:
178 headers = {**headers, **default_headers}
179 elif set_default_headers is not None:
180 headers = set_default_headers
181
182 params = self._custom_query
183 if default_query is not None:
184 params = {**params, **default_query}
185 elif set_default_query is not None:
186 params = set_default_query
187
188 http_client = http_client or self._client
189 return self.__class__(
190 api_key=api_key or self.api_key,
191 organization=organization or self.organization,
192 base_url=base_url or str(self.base_url),
193 timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
194 http_client=http_client,
195 max_retries=max_retries if is_given(max_retries) else self.max_retries,
196 default_headers=headers,
197 default_query=params,
198 )
199
200 # Alias for `copy` for nicer inline usage, e.g.
201 # client.with_options(timeout=10).foo.create(...)
202 with_options = copy
203
204 def __del__(self) -> None:
205 if not hasattr(self, "_has_custom_http_client") or not hasattr(self, "close"):
206 # this can happen if the '__init__' method raised an error
207 return
208
209 if self._has_custom_http_client:
210 return
211
212 self.close()
213
214 @override
215 def _make_status_error(
216 self,
217 err_msg: str,
218 *,
219 body: object,
220 response: httpx.Response,
221 ) -> APIStatusError:
222 if response.status_code == 400:
223 return _exceptions.BadRequestError(err_msg, response=response, body=body)
224
225 if response.status_code == 401:
226 return _exceptions.AuthenticationError(err_msg, response=response, body=body)
227
228 if response.status_code == 403:
229 return _exceptions.PermissionDeniedError(err_msg, response=response, body=body)
230
231 if response.status_code == 404:
232 return _exceptions.NotFoundError(err_msg, response=response, body=body)
233
234 if response.status_code == 409:
235 return _exceptions.ConflictError(err_msg, response=response, body=body)
236
237 if response.status_code == 422:
238 return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body)
239
240 if response.status_code == 429:
241 return _exceptions.RateLimitError(err_msg, response=response, body=body)
242
243 if response.status_code >= 500:
244 return _exceptions.InternalServerError(err_msg, response=response, body=body)
245 return APIStatusError(err_msg, response=response, body=body)
246
247
248class AsyncOpenAI(AsyncAPIClient):
249 completions: resources.AsyncCompletions
250 chat: resources.AsyncChat
251 edits: resources.AsyncEdits
252 embeddings: resources.AsyncEmbeddings
253 files: resources.AsyncFiles
254 images: resources.AsyncImages
255 audio: resources.AsyncAudio
256 moderations: resources.AsyncModerations
257 models: resources.AsyncModels
258 fine_tuning: resources.AsyncFineTuning
259 fine_tunes: resources.AsyncFineTunes
260 with_raw_response: AsyncOpenAIWithRawResponse
261
262 # client options
263 api_key: str
264 organization: str | None
265
266 def __init__(
267 self,
268 *,
269 api_key: str | None = None,
270 organization: str | None = None,
271 base_url: str | httpx.URL | None = None,
272 timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
273 max_retries: int = DEFAULT_MAX_RETRIES,
274 default_headers: Mapping[str, str] | None = None,
275 default_query: Mapping[str, object] | None = None,
276 # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details.
277 http_client: httpx.AsyncClient | None = None,
278 # Enable or disable schema validation for data returned by the API.
279 # When enabled an error APIResponseValidationError is raised
280 # if the API responds with invalid data for the expected schema.
281 #
282 # This parameter may be removed or changed in the future.
283 # If you rely on this feature, please open a GitHub issue
284 # outlining your use-case to help us decide if it should be
285 # part of our public interface in the future.
286 _strict_response_validation: bool = False,
287 ) -> None:
288 """Construct a new async openai client instance.
289
290 This automatically infers the following arguments from their corresponding environment variables if they are not provided:
291 - `api_key` from `OPENAI_API_KEY`
292 - `organization` from `OPENAI_ORG_ID`
293 """
294 if api_key is None:
295 api_key = os.environ.get("OPENAI_API_KEY")
296 if api_key is None:
297 raise OpenAIError(
298 "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable"
299 )
300 self.api_key = api_key
301
302 if organization is None:
303 organization = os.environ.get("OPENAI_ORG_ID")
304 self.organization = organization
305
306 if base_url is None:
307 base_url = f"https://api.openai.com/v1"
308
309 super().__init__(
310 version=__version__,
311 base_url=base_url,
312 max_retries=max_retries,
313 timeout=timeout,
314 http_client=http_client,
315 custom_headers=default_headers,
316 custom_query=default_query,
317 _strict_response_validation=_strict_response_validation,
318 )
319
320 self._default_stream_cls = AsyncStream
321
322 self.completions = resources.AsyncCompletions(self)
323 self.chat = resources.AsyncChat(self)
324 self.edits = resources.AsyncEdits(self)
325 self.embeddings = resources.AsyncEmbeddings(self)
326 self.files = resources.AsyncFiles(self)
327 self.images = resources.AsyncImages(self)
328 self.audio = resources.AsyncAudio(self)
329 self.moderations = resources.AsyncModerations(self)
330 self.models = resources.AsyncModels(self)
331 self.fine_tuning = resources.AsyncFineTuning(self)
332 self.fine_tunes = resources.AsyncFineTunes(self)
333 self.with_raw_response = AsyncOpenAIWithRawResponse(self)
334
335 @property
336 @override
337 def qs(self) -> Querystring:
338 return Querystring(array_format="comma")
339
340 @property
341 @override
342 def auth_headers(self) -> dict[str, str]:
343 api_key = self.api_key
344 return {"Authorization": f"Bearer {api_key}"}
345
346 @property
347 @override
348 def default_headers(self) -> dict[str, str | Omit]:
349 return {
350 **super().default_headers,
351 "OpenAI-Organization": self.organization if self.organization is not None else Omit(),
352 **self._custom_headers,
353 }
354
355 def copy(
356 self,
357 *,
358 api_key: str | None = None,
359 organization: str | None = None,
360 base_url: str | httpx.URL | None = None,
361 timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
362 http_client: httpx.AsyncClient | None = None,
363 max_retries: int | NotGiven = NOT_GIVEN,
364 default_headers: Mapping[str, str] | None = None,
365 set_default_headers: Mapping[str, str] | None = None,
366 default_query: Mapping[str, object] | None = None,
367 set_default_query: Mapping[str, object] | None = None,
368 ) -> AsyncOpenAI:
369 """
370 Create a new client instance re-using the same options given to the current client with optional overriding.
371
372 It should be noted that this does not share the underlying httpx client class which may lead
373 to performance issues.
374 """
375 if default_headers is not None and set_default_headers is not None:
376 raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")
377
378 if default_query is not None and set_default_query is not None:
379 raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive")
380
381 headers = self._custom_headers
382 if default_headers is not None:
383 headers = {**headers, **default_headers}
384 elif set_default_headers is not None:
385 headers = set_default_headers
386
387 params = self._custom_query
388 if default_query is not None:
389 params = {**params, **default_query}
390 elif set_default_query is not None:
391 params = set_default_query
392
393 http_client = http_client or self._client
394 return self.__class__(
395 api_key=api_key or self.api_key,
396 organization=organization or self.organization,
397 base_url=base_url or str(self.base_url),
398 timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
399 http_client=http_client,
400 max_retries=max_retries if is_given(max_retries) else self.max_retries,
401 default_headers=headers,
402 default_query=params,
403 )
404
405 # Alias for `copy` for nicer inline usage, e.g.
406 # client.with_options(timeout=10).foo.create(...)
407 with_options = copy
408
409 def __del__(self) -> None:
410 if not hasattr(self, "_has_custom_http_client") or not hasattr(self, "close"):
411 # this can happen if the '__init__' method raised an error
412 return
413
414 if self._has_custom_http_client:
415 return
416
417 try:
418 asyncio.get_running_loop().create_task(self.close())
419 except Exception:
420 pass
421
422 @override
423 def _make_status_error(
424 self,
425 err_msg: str,
426 *,
427 body: object,
428 response: httpx.Response,
429 ) -> APIStatusError:
430 if response.status_code == 400:
431 return _exceptions.BadRequestError(err_msg, response=response, body=body)
432
433 if response.status_code == 401:
434 return _exceptions.AuthenticationError(err_msg, response=response, body=body)
435
436 if response.status_code == 403:
437 return _exceptions.PermissionDeniedError(err_msg, response=response, body=body)
438
439 if response.status_code == 404:
440 return _exceptions.NotFoundError(err_msg, response=response, body=body)
441
442 if response.status_code == 409:
443 return _exceptions.ConflictError(err_msg, response=response, body=body)
444
445 if response.status_code == 422:
446 return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body)
447
448 if response.status_code == 429:
449 return _exceptions.RateLimitError(err_msg, response=response, body=body)
450
451 if response.status_code >= 500:
452 return _exceptions.InternalServerError(err_msg, response=response, body=body)
453 return APIStatusError(err_msg, response=response, body=body)
454
455
456class OpenAIWithRawResponse:
457 def __init__(self, client: OpenAI) -> None:
458 self.completions = resources.CompletionsWithRawResponse(client.completions)
459 self.chat = resources.ChatWithRawResponse(client.chat)
460 self.edits = resources.EditsWithRawResponse(client.edits)
461 self.embeddings = resources.EmbeddingsWithRawResponse(client.embeddings)
462 self.files = resources.FilesWithRawResponse(client.files)
463 self.images = resources.ImagesWithRawResponse(client.images)
464 self.audio = resources.AudioWithRawResponse(client.audio)
465 self.moderations = resources.ModerationsWithRawResponse(client.moderations)
466 self.models = resources.ModelsWithRawResponse(client.models)
467 self.fine_tuning = resources.FineTuningWithRawResponse(client.fine_tuning)
468 self.fine_tunes = resources.FineTunesWithRawResponse(client.fine_tunes)
469
470
471class AsyncOpenAIWithRawResponse:
472 def __init__(self, client: AsyncOpenAI) -> None:
473 self.completions = resources.AsyncCompletionsWithRawResponse(client.completions)
474 self.chat = resources.AsyncChatWithRawResponse(client.chat)
475 self.edits = resources.AsyncEditsWithRawResponse(client.edits)
476 self.embeddings = resources.AsyncEmbeddingsWithRawResponse(client.embeddings)
477 self.files = resources.AsyncFilesWithRawResponse(client.files)
478 self.images = resources.AsyncImagesWithRawResponse(client.images)
479 self.audio = resources.AsyncAudioWithRawResponse(client.audio)
480 self.moderations = resources.AsyncModerationsWithRawResponse(client.moderations)
481 self.models = resources.AsyncModelsWithRawResponse(client.models)
482 self.fine_tuning = resources.AsyncFineTuningWithRawResponse(client.fine_tuning)
483 self.fine_tunes = resources.AsyncFineTunesWithRawResponse(client.fine_tunes)
484
485
486Client = OpenAI
487
488AsyncClient = AsyncOpenAI
489