openai/openai-python

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.3.5

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/openai/_client.py

498lines · modecode

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