sswayam commited on
Commit
459e4cf
·
verified ·
1 Parent(s): fd03bc2

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .venv311/Lib/site-packages/openai-2.36.0.dist-info/INSTALLER +1 -0
  2. .venv311/Lib/site-packages/openai-2.36.0.dist-info/METADATA +1012 -0
  3. .venv311/Lib/site-packages/openai-2.36.0.dist-info/RECORD +0 -0
  4. .venv311/Lib/site-packages/openai-2.36.0.dist-info/REQUESTED +0 -0
  5. .venv311/Lib/site-packages/openai-2.36.0.dist-info/WHEEL +4 -0
  6. .venv311/Lib/site-packages/openai-2.36.0.dist-info/licenses/LICENSE +201 -0
  7. .venv311/Lib/site-packages/openai/_extras/__init__.py +3 -0
  8. .venv311/Lib/site-packages/openai/_extras/_common.py +21 -0
  9. .venv311/Lib/site-packages/openai/_extras/numpy_proxy.py +37 -0
  10. .venv311/Lib/site-packages/openai/_extras/pandas_proxy.py +28 -0
  11. .venv311/Lib/site-packages/openai/_extras/sounddevice_proxy.py +28 -0
  12. .venv311/Lib/site-packages/openai/_utils/__init__.py +67 -0
  13. .venv311/Lib/site-packages/openai/_utils/_compat.py +45 -0
  14. .venv311/Lib/site-packages/openai/_utils/_datetime_parse.py +136 -0
  15. .venv311/Lib/site-packages/openai/_utils/_json.py +35 -0
  16. .venv311/Lib/site-packages/openai/_utils/_logs.py +42 -0
  17. .venv311/Lib/site-packages/openai/_utils/_path.py +127 -0
  18. .venv311/Lib/site-packages/openai/_utils/_proxy.py +65 -0
  19. .venv311/Lib/site-packages/openai/_utils/_reflection.py +45 -0
  20. .venv311/Lib/site-packages/openai/_utils/_resources_proxy.py +24 -0
  21. .venv311/Lib/site-packages/openai/_utils/_streams.py +12 -0
  22. .venv311/Lib/site-packages/openai/_utils/_sync.py +58 -0
  23. .venv311/Lib/site-packages/openai/_utils/_transform.py +457 -0
  24. .venv311/Lib/site-packages/openai/_utils/_typing.py +156 -0
  25. .venv311/Lib/site-packages/openai/_utils/_utils.py +449 -0
  26. .venv311/Lib/site-packages/openai/types/admin/organization/projects/__init__.py +34 -0
  27. .venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/__init__.py +9 -0
  28. .venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_create_params.py +14 -0
  29. .venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_create_response.py +40 -0
  30. .venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_delete_response.py +18 -0
  31. .venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_list_params.py +24 -0
  32. .venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_list_response.py +46 -0
  33. .venv311/Lib/site-packages/openai/types/admin/organization/projects/user_delete_response.py +15 -0
  34. .venv311/Lib/site-packages/openai/types/admin/organization/projects/user_update_params.py +15 -0
  35. .venv311/Lib/site-packages/openai/types/admin/organization/projects/users/__init__.py +9 -0
  36. .venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_create_params.py +14 -0
  37. .venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_create_response.py +22 -0
  38. .venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_delete_response.py +18 -0
  39. .venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_list_params.py +24 -0
  40. .venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_list_response.py +46 -0
  41. .venv311/Lib/site-packages/openai/types/admin/organization/users/__init__.py +9 -0
  42. .venv311/Lib/site-packages/openai/types/admin/organization/users/role_create_params.py +12 -0
  43. .venv311/Lib/site-packages/openai/types/admin/organization/users/role_create_response.py +22 -0
  44. .venv311/Lib/site-packages/openai/types/admin/organization/users/role_delete_response.py +18 -0
  45. .venv311/Lib/site-packages/openai/types/admin/organization/users/role_list_params.py +22 -0
  46. .venv311/Lib/site-packages/openai/types/admin/organization/users/role_list_response.py +46 -0
  47. .venv311/Lib/site-packages/openai/types/audio/__init__.py +23 -0
  48. .venv311/Lib/site-packages/openai/types/audio/speech_create_params.py +68 -0
  49. .venv311/Lib/site-packages/openai/types/audio/speech_model.py +7 -0
  50. .venv311/Lib/site-packages/openai/types/audio/transcription.py +81 -0
.venv311/Lib/site-packages/openai-2.36.0.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
.venv311/Lib/site-packages/openai-2.36.0.dist-info/METADATA ADDED
@@ -0,0 +1,1012 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.3
2
+ Name: openai
3
+ Version: 2.36.0
4
+ Summary: The official Python library for the openai API
5
+ Project-URL: Homepage, https://github.com/openai/openai-python
6
+ Project-URL: Repository, https://github.com/openai/openai-python
7
+ Author-email: OpenAI <support@openai.com>
8
+ License: Apache-2.0
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Operating System :: MacOS
12
+ Classifier: Operating System :: Microsoft :: Windows
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Operating System :: POSIX
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.9
25
+ Requires-Dist: anyio<5,>=3.5.0
26
+ Requires-Dist: distro<2,>=1.7.0
27
+ Requires-Dist: httpx<1,>=0.23.0
28
+ Requires-Dist: jiter<1,>=0.10.0
29
+ Requires-Dist: pydantic<3,>=1.9.0
30
+ Requires-Dist: sniffio
31
+ Requires-Dist: tqdm>4
32
+ Requires-Dist: typing-extensions<5,>=4.11
33
+ Requires-Dist: typing-extensions<5,>=4.14
34
+ Provides-Extra: aiohttp
35
+ Requires-Dist: aiohttp; extra == 'aiohttp'
36
+ Requires-Dist: httpx-aiohttp>=0.1.9; extra == 'aiohttp'
37
+ Provides-Extra: datalib
38
+ Requires-Dist: numpy>=1; extra == 'datalib'
39
+ Requires-Dist: pandas-stubs>=1.1.0.11; extra == 'datalib'
40
+ Requires-Dist: pandas>=1.2.3; extra == 'datalib'
41
+ Provides-Extra: realtime
42
+ Requires-Dist: websockets<16,>=13; extra == 'realtime'
43
+ Provides-Extra: voice-helpers
44
+ Requires-Dist: numpy>=2.0.2; extra == 'voice-helpers'
45
+ Requires-Dist: sounddevice>=0.5.1; extra == 'voice-helpers'
46
+ Description-Content-Type: text/markdown
47
+
48
+ # OpenAI Python API library
49
+
50
+ <!-- prettier-ignore -->
51
+ [![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/)
52
+
53
+ The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.9+
54
+ application. The library includes type definitions for all request params and response fields,
55
+ and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
56
+
57
+ It is generated from our [OpenAPI specification](https://github.com/openai/openai-openapi) with [Stainless](https://stainlessapi.com/).
58
+
59
+ ## Documentation
60
+
61
+ The 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](https://github.com/openai/openai-python/tree/main/api.md).
62
+
63
+ ## Installation
64
+
65
+ ```sh
66
+ # install from PyPI
67
+ pip install openai
68
+ ```
69
+
70
+ ## Usage
71
+
72
+ The full API of this library can be found in [api.md](https://github.com/openai/openai-python/tree/main/api.md).
73
+
74
+ The 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.
75
+
76
+ ```python
77
+ import os
78
+ from openai import OpenAI
79
+
80
+ client = OpenAI(
81
+ # This is the default and can be omitted
82
+ api_key=os.environ.get("OPENAI_API_KEY"),
83
+ )
84
+
85
+ response = client.responses.create(
86
+ model="gpt-5.2",
87
+ instructions="You are a coding assistant that talks like a pirate.",
88
+ input="How do I check if a Python object is an instance of a class?",
89
+ )
90
+
91
+ print(response.output_text)
92
+ ```
93
+
94
+ The 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.
95
+
96
+ ```python
97
+ from openai import OpenAI
98
+
99
+ client = OpenAI()
100
+
101
+ completion = client.chat.completions.create(
102
+ model="gpt-5.2",
103
+ messages=[
104
+ {"role": "developer", "content": "Talk like a pirate."},
105
+ {
106
+ "role": "user",
107
+ "content": "How do I check if a Python object is an instance of a class?",
108
+ },
109
+ ],
110
+ )
111
+
112
+ print(completion.choices[0].message.content)
113
+ ```
114
+
115
+ While you can provide an `api_key` keyword argument,
116
+ we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)
117
+ to add `OPENAI_API_KEY="My API Key"` to your `.env` file
118
+ so that your API key is not stored in source control.
119
+ [Get an API key here](https://platform.openai.com/settings/organization/api-keys).
120
+
121
+ ### Workload Identity Authentication
122
+
123
+ For secure, automated environments like cloud-managed Kubernetes, Azure, and Google Cloud Platform, you can use workload identity authentication with short-lived tokens from cloud identity providers instead of long-lived API keys.
124
+
125
+ #### Kubernetes (service account tokens)
126
+
127
+ ```python
128
+ from openai import OpenAI
129
+ from openai.auth import k8s_service_account_token_provider
130
+
131
+ client = OpenAI(
132
+ workload_identity={
133
+ "client_id": "your-client-id",
134
+ "identity_provider_id": "idp-123",
135
+ "service_account_id": "sa-456",
136
+ "provider": k8s_service_account_token_provider(
137
+ "/var/run/secrets/kubernetes.io/serviceaccount/token"
138
+ ),
139
+ },
140
+ organization="org-xyz",
141
+ project="proj-abc",
142
+ )
143
+
144
+ response = client.chat.completions.create(
145
+ model="gpt-4",
146
+ messages=[{"role": "user", "content": "Hello!"}],
147
+ )
148
+ ```
149
+
150
+ #### Azure (managed identity)
151
+
152
+ ```python
153
+ from openai import OpenAI
154
+ from openai.auth import azure_managed_identity_token_provider
155
+
156
+ client = OpenAI(
157
+ workload_identity={
158
+ "client_id": "your-client-id",
159
+ "identity_provider_id": "idp-123",
160
+ "service_account_id": "sa-456",
161
+ "provider": azure_managed_identity_token_provider(
162
+ resource="https://management.azure.com/",
163
+ ),
164
+ },
165
+ )
166
+ ```
167
+
168
+ #### Google Cloud Platform (compute engine metadata)
169
+
170
+ ```python
171
+ from openai import OpenAI
172
+ from openai.auth import gcp_id_token_provider
173
+
174
+ client = OpenAI(
175
+ workload_identity={
176
+ "client_id": "your-client-id",
177
+ "identity_provider_id": "idp-123",
178
+ "service_account_id": "sa-456",
179
+ "provider": gcp_id_token_provider(audience="https://api.openai.com/v1"),
180
+ },
181
+ )
182
+ ```
183
+
184
+ #### Custom subject token provider
185
+
186
+ ```python
187
+ from openai import OpenAI
188
+
189
+
190
+ def get_custom_token() -> str:
191
+ return "your-jwt-token"
192
+
193
+
194
+ client = OpenAI(
195
+ workload_identity={
196
+ "client_id": "your-client-id",
197
+ "identity_provider_id": "idp-123",
198
+ "service_account_id": "sa-456",
199
+ "provider": {
200
+ "token_type": "jwt",
201
+ "get_token": get_custom_token,
202
+ },
203
+ }
204
+ )
205
+ ```
206
+
207
+ You can also customize the token refresh buffer (default is 1200 seconds (20 minutes) before expiration):
208
+
209
+ ```python
210
+ from openai import OpenAI
211
+ from openai.auth import k8s_service_account_token_provider
212
+
213
+ client = OpenAI(
214
+ workload_identity={
215
+ "client_id": "your-client-id",
216
+ "identity_provider_id": "idp-123",
217
+ "service_account_id": "sa-456",
218
+ "provider": k8s_service_account_token_provider("/var/token"),
219
+ "refresh_buffer_seconds": 120.0,
220
+ }
221
+ )
222
+ ```
223
+
224
+ ### Vision
225
+
226
+ With an image URL:
227
+
228
+ ```python
229
+ prompt = "What is in this image?"
230
+ img_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/2023_06_08_Raccoon1.jpg/1599px-2023_06_08_Raccoon1.jpg"
231
+
232
+ response = client.responses.create(
233
+ model="gpt-5.2",
234
+ input=[
235
+ {
236
+ "role": "user",
237
+ "content": [
238
+ {"type": "input_text", "text": prompt},
239
+ {"type": "input_image", "image_url": f"{img_url}"},
240
+ ],
241
+ }
242
+ ],
243
+ )
244
+ ```
245
+
246
+ With the image as a base64 encoded string:
247
+
248
+ ```python
249
+ import base64
250
+ from openai import OpenAI
251
+
252
+ client = OpenAI()
253
+
254
+ prompt = "What is in this image?"
255
+ with open("path/to/image.png", "rb") as image_file:
256
+ b64_image = base64.b64encode(image_file.read()).decode("utf-8")
257
+
258
+ response = client.responses.create(
259
+ model="gpt-5.2",
260
+ input=[
261
+ {
262
+ "role": "user",
263
+ "content": [
264
+ {"type": "input_text", "text": prompt},
265
+ {"type": "input_image", "image_url": f"data:image/png;base64,{b64_image}"},
266
+ ],
267
+ }
268
+ ],
269
+ )
270
+ ```
271
+
272
+ ## Async usage
273
+
274
+ Simply import `AsyncOpenAI` instead of `OpenAI` and use `await` with each API call:
275
+
276
+ ```python
277
+ import os
278
+ import asyncio
279
+ from openai import AsyncOpenAI
280
+
281
+ client = AsyncOpenAI(
282
+ # This is the default and can be omitted
283
+ api_key=os.environ.get("OPENAI_API_KEY"),
284
+ )
285
+
286
+
287
+ async def main() -> None:
288
+ response = await client.responses.create(
289
+ model="gpt-5.2", input="Explain disestablishmentarianism to a smart five year old."
290
+ )
291
+ print(response.output_text)
292
+
293
+
294
+ asyncio.run(main())
295
+ ```
296
+
297
+ Functionality between the synchronous and asynchronous clients is otherwise identical.
298
+
299
+ ### With aiohttp
300
+
301
+ By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
302
+
303
+ You can enable this by installing `aiohttp`:
304
+
305
+ ```sh
306
+ # install from PyPI
307
+ pip install openai[aiohttp]
308
+ ```
309
+
310
+ Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
311
+
312
+ ```python
313
+ import os
314
+ import asyncio
315
+ from openai import DefaultAioHttpClient
316
+ from openai import AsyncOpenAI
317
+
318
+
319
+ async def main() -> None:
320
+ async with AsyncOpenAI(
321
+ api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted
322
+ http_client=DefaultAioHttpClient(),
323
+ ) as client:
324
+ chat_completion = await client.chat.completions.create(
325
+ messages=[
326
+ {
327
+ "role": "user",
328
+ "content": "Say this is a test",
329
+ }
330
+ ],
331
+ model="gpt-5.2",
332
+ )
333
+
334
+
335
+ asyncio.run(main())
336
+ ```
337
+
338
+ ## Streaming responses
339
+
340
+ We provide support for streaming responses using Server Side Events (SSE).
341
+
342
+ ```python
343
+ from openai import OpenAI
344
+
345
+ client = OpenAI()
346
+
347
+ stream = client.responses.create(
348
+ model="gpt-5.2",
349
+ input="Write a one-sentence bedtime story about a unicorn.",
350
+ stream=True,
351
+ )
352
+
353
+ for event in stream:
354
+ print(event)
355
+ ```
356
+
357
+ The async client uses the exact same interface.
358
+
359
+ ```python
360
+ import asyncio
361
+ from openai import AsyncOpenAI
362
+
363
+ client = AsyncOpenAI()
364
+
365
+
366
+ async def main():
367
+ stream = await client.responses.create(
368
+ model="gpt-5.2",
369
+ input="Write a one-sentence bedtime story about a unicorn.",
370
+ stream=True,
371
+ )
372
+
373
+ async for event in stream:
374
+ print(event)
375
+
376
+
377
+ asyncio.run(main())
378
+ ```
379
+
380
+ ## Realtime API
381
+
382
+ The 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.
383
+
384
+ Under the hood the SDK uses the [`websockets`](https://websockets.readthedocs.io/en/stable/) library to manage connections.
385
+
386
+ The 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).
387
+
388
+ Basic text based example:
389
+
390
+ ```py
391
+ import asyncio
392
+ from openai import AsyncOpenAI
393
+
394
+ async def main():
395
+ client = AsyncOpenAI()
396
+
397
+ async with client.realtime.connect(model="gpt-realtime") as connection:
398
+ await connection.session.update(
399
+ session={"type": "realtime", "output_modalities": ["text"]}
400
+ )
401
+
402
+ await connection.conversation.item.create(
403
+ item={
404
+ "type": "message",
405
+ "role": "user",
406
+ "content": [{"type": "input_text", "text": "Say hello!"}],
407
+ }
408
+ )
409
+ await connection.response.create()
410
+
411
+ async for event in connection:
412
+ if event.type == "response.output_text.delta":
413
+ print(event.delta, flush=True, end="")
414
+
415
+ elif event.type == "response.output_text.done":
416
+ print()
417
+
418
+ elif event.type == "response.done":
419
+ break
420
+
421
+ asyncio.run(main())
422
+ ```
423
+
424
+ However 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.
425
+
426
+ ### Realtime error handling
427
+
428
+ Whenever 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.
429
+
430
+ ```py
431
+ client = AsyncOpenAI()
432
+
433
+ async with client.realtime.connect(model="gpt-realtime") as connection:
434
+ ...
435
+ async for event in connection:
436
+ if event.type == 'error':
437
+ print(event.error.type)
438
+ print(event.error.code)
439
+ print(event.error.event_id)
440
+ print(event.error.message)
441
+ ```
442
+
443
+ ## Using types
444
+
445
+ Nested 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:
446
+
447
+ - Serializing back into JSON, `model.to_json()`
448
+ - Converting to a dictionary, `model.to_dict()`
449
+
450
+ Typed 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`.
451
+
452
+ ## Pagination
453
+
454
+ List methods in the OpenAI API are paginated.
455
+
456
+ This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually:
457
+
458
+ ```python
459
+ from openai import OpenAI
460
+
461
+ client = OpenAI()
462
+
463
+ all_jobs = []
464
+ # Automatically fetches more pages as needed.
465
+ for job in client.fine_tuning.jobs.list(
466
+ limit=20,
467
+ ):
468
+ # Do something with job here
469
+ all_jobs.append(job)
470
+ print(all_jobs)
471
+ ```
472
+
473
+ Or, asynchronously:
474
+
475
+ ```python
476
+ import asyncio
477
+ from openai import AsyncOpenAI
478
+
479
+ client = AsyncOpenAI()
480
+
481
+
482
+ async def main() -> None:
483
+ all_jobs = []
484
+ # Iterate through items across all pages, issuing requests as needed.
485
+ async for job in client.fine_tuning.jobs.list(
486
+ limit=20,
487
+ ):
488
+ all_jobs.append(job)
489
+ print(all_jobs)
490
+
491
+
492
+ asyncio.run(main())
493
+ ```
494
+
495
+ Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages:
496
+
497
+ ```python
498
+ first_page = await client.fine_tuning.jobs.list(
499
+ limit=20,
500
+ )
501
+ if first_page.has_next_page():
502
+ print(f"will fetch next page using these details: {first_page.next_page_info()}")
503
+ next_page = await first_page.get_next_page()
504
+ print(f"number of items we just fetched: {len(next_page.data)}")
505
+
506
+ # Remove `await` for non-async usage.
507
+ ```
508
+
509
+ Or just work directly with the returned data:
510
+
511
+ ```python
512
+ first_page = await client.fine_tuning.jobs.list(
513
+ limit=20,
514
+ )
515
+
516
+ print(f"next page cursor: {first_page.after}") # => "next page cursor: ..."
517
+ for job in first_page.data:
518
+ print(job.id)
519
+
520
+ # Remove `await` for non-async usage.
521
+ ```
522
+
523
+ ## Nested params
524
+
525
+ Nested parameters are dictionaries, typed using `TypedDict`, for example:
526
+
527
+ ```python
528
+ from openai import OpenAI
529
+
530
+ client = OpenAI()
531
+
532
+ response = client.chat.responses.create(
533
+ input=[
534
+ {
535
+ "role": "user",
536
+ "content": "How much ?",
537
+ }
538
+ ],
539
+ model="gpt-5.2",
540
+ response_format={"type": "json_object"},
541
+ )
542
+ ```
543
+
544
+ ## File uploads
545
+
546
+ Request 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)`.
547
+
548
+ ```python
549
+ from pathlib import Path
550
+ from openai import OpenAI
551
+
552
+ client = OpenAI()
553
+
554
+ client.files.create(
555
+ file=Path("input.jsonl"),
556
+ purpose="fine-tune",
557
+ )
558
+ ```
559
+
560
+ The 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.
561
+
562
+ ## Webhook Verification
563
+
564
+ Verifying webhook signatures is _optional but encouraged_.
565
+
566
+ For more information about webhooks, see [the API docs](https://platform.openai.com/docs/guides/webhooks).
567
+
568
+ ### Parsing webhook payloads
569
+
570
+ For 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.
571
+
572
+ Note 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.
573
+
574
+ ```python
575
+ from openai import OpenAI
576
+ from flask import Flask, request
577
+
578
+ app = Flask(__name__)
579
+ client = OpenAI() # OPENAI_WEBHOOK_SECRET environment variable is used by default
580
+
581
+
582
+ @app.route("/webhook", methods=["POST"])
583
+ def webhook():
584
+ request_body = request.get_data(as_text=True)
585
+
586
+ try:
587
+ event = client.webhooks.unwrap(request_body, request.headers)
588
+
589
+ if event.type == "response.completed":
590
+ print("Response completed:", event.data)
591
+ elif event.type == "response.failed":
592
+ print("Response failed:", event.data)
593
+ else:
594
+ print("Unhandled event type:", event.type)
595
+
596
+ return "ok"
597
+ except Exception as e:
598
+ print("Invalid signature:", e)
599
+ return "Invalid signature", 400
600
+
601
+
602
+ if __name__ == "__main__":
603
+ app.run(port=8000)
604
+ ```
605
+
606
+ ### Verifying webhook payloads directly
607
+
608
+ In 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.
609
+
610
+ Note 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.
611
+
612
+ ```python
613
+ import json
614
+ from openai import OpenAI
615
+ from flask import Flask, request
616
+
617
+ app = Flask(__name__)
618
+ client = OpenAI() # OPENAI_WEBHOOK_SECRET environment variable is used by default
619
+
620
+
621
+ @app.route("/webhook", methods=["POST"])
622
+ def webhook():
623
+ request_body = request.get_data(as_text=True)
624
+
625
+ try:
626
+ client.webhooks.verify_signature(request_body, request.headers)
627
+
628
+ # Parse the body after verification
629
+ event = json.loads(request_body)
630
+ print("Verified event:", event)
631
+
632
+ return "ok"
633
+ except Exception as e:
634
+ print("Invalid signature:", e)
635
+ return "Invalid signature", 400
636
+
637
+
638
+ if __name__ == "__main__":
639
+ app.run(port=8000)
640
+ ```
641
+
642
+ ## Handling errors
643
+
644
+ When 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.
645
+
646
+ When the API returns a non-success status code (that is, 4xx or 5xx
647
+ response), a subclass of `openai.APIStatusError` is raised, containing `status_code` and `response` properties.
648
+
649
+ All errors inherit from `openai.APIError`.
650
+
651
+ ```python
652
+ import openai
653
+ from openai import OpenAI
654
+
655
+ client = OpenAI()
656
+
657
+ try:
658
+ client.fine_tuning.jobs.create(
659
+ model="gpt-4o",
660
+ training_file="file-abc123",
661
+ )
662
+ except openai.APIConnectionError as e:
663
+ print("The server could not be reached")
664
+ print(e.__cause__) # an underlying Exception, likely raised within httpx.
665
+ except openai.RateLimitError as e:
666
+ print("A 429 status code was received; we should back off a bit.")
667
+ except openai.APIStatusError as e:
668
+ print("Another non-200-range status code was received")
669
+ print(e.status_code)
670
+ print(e.response)
671
+ ```
672
+
673
+ Error codes are as follows:
674
+
675
+ | Status Code | Error Type |
676
+ | ----------- | -------------------------- |
677
+ | 400 | `BadRequestError` |
678
+ | 401 | `AuthenticationError` |
679
+ | 403 | `PermissionDeniedError` |
680
+ | 404 | `NotFoundError` |
681
+ | 422 | `UnprocessableEntityError` |
682
+ | 429 | `RateLimitError` |
683
+ | >=500 | `InternalServerError` |
684
+ | N/A | `APIConnectionError` |
685
+
686
+ ## Request IDs
687
+
688
+ > For more information on debugging requests, see [these docs](https://platform.openai.com/docs/api-reference/debugging-requests)
689
+
690
+ All 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.
691
+
692
+ ```python
693
+ response = await client.responses.create(
694
+ model="gpt-5.2",
695
+ input="Say 'this is a test'.",
696
+ )
697
+ print(response._request_id) # req_123
698
+ ```
699
+
700
+ Note that unlike other properties that use an `_` prefix, the `_request_id` property
701
+ _is_ public. Unless documented otherwise, _all_ other `_` prefix properties,
702
+ methods and modules are _private_.
703
+
704
+ > [!IMPORTANT]
705
+ > If you need to access request IDs for failed requests you must catch the `APIStatusError` exception
706
+
707
+ ```python
708
+ import openai
709
+
710
+ try:
711
+ completion = await client.chat.completions.create(
712
+ messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-5.2"
713
+ )
714
+ except openai.APIStatusError as exc:
715
+ print(exc.request_id) # req_123
716
+ raise exc
717
+ ```
718
+
719
+ ## Retries
720
+
721
+ Certain errors are automatically retried 2 times by default, with a short exponential backoff.
722
+ Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,
723
+ 429 Rate Limit, and >=500 Internal errors are all retried by default.
724
+
725
+ You can use the `max_retries` option to configure or disable retry settings:
726
+
727
+ ```python
728
+ from openai import OpenAI
729
+
730
+ # Configure the default for all requests:
731
+ client = OpenAI(
732
+ # default is 2
733
+ max_retries=0,
734
+ )
735
+
736
+ # Or, configure per-request:
737
+ client.with_options(max_retries=5).chat.completions.create(
738
+ messages=[
739
+ {
740
+ "role": "user",
741
+ "content": "How can I get the name of the current day in JavaScript?",
742
+ }
743
+ ],
744
+ model="gpt-5.2",
745
+ )
746
+ ```
747
+
748
+ ## Timeouts
749
+
750
+ By default requests time out after 10 minutes. You can configure this with a `timeout` option,
751
+ which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:
752
+
753
+ ```python
754
+ from openai import OpenAI
755
+
756
+ # Configure the default for all requests:
757
+ client = OpenAI(
758
+ # 20 seconds (default is 10 minutes)
759
+ timeout=20.0,
760
+ )
761
+
762
+ # More granular control:
763
+ client = OpenAI(
764
+ timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
765
+ )
766
+
767
+ # Override per-request:
768
+ client.with_options(timeout=5.0).chat.completions.create(
769
+ messages=[
770
+ {
771
+ "role": "user",
772
+ "content": "How can I list all files in a directory using Python?",
773
+ }
774
+ ],
775
+ model="gpt-5.2",
776
+ )
777
+ ```
778
+
779
+ On timeout, an `APITimeoutError` is thrown.
780
+
781
+ Note that requests that time out are [retried twice by default](https://github.com/openai/openai-python/tree/main/#retries).
782
+
783
+ ## Advanced
784
+
785
+ ### Logging
786
+
787
+ We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.
788
+
789
+ You can enable logging by setting the environment variable `OPENAI_LOG` to `info`.
790
+
791
+ ```shell
792
+ $ export OPENAI_LOG=info
793
+ ```
794
+
795
+ Or to `debug` for more verbose logging.
796
+
797
+ ### How to tell whether `None` means `null` or missing
798
+
799
+ In 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`:
800
+
801
+ ```py
802
+ if response.my_field is None:
803
+ if 'my_field' not in response.model_fields_set:
804
+ print('Got json like {}, without a "my_field" key present at all.')
805
+ else:
806
+ print('Got json like {"my_field": null}.')
807
+ ```
808
+
809
+ ### Accessing raw response data (e.g. headers)
810
+
811
+ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g.,
812
+
813
+ ```py
814
+ from openai import OpenAI
815
+
816
+ client = OpenAI()
817
+ response = client.chat.completions.with_raw_response.create(
818
+ messages=[{
819
+ "role": "user",
820
+ "content": "Say this is a test",
821
+ }],
822
+ model="gpt-5.2",
823
+ )
824
+ print(response.headers.get('X-My-Header'))
825
+
826
+ completion = response.parse() # get the object that `chat.completions.create()` would have returned
827
+ print(completion)
828
+ ```
829
+
830
+ These 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.
831
+
832
+ For the sync client this will mostly be the same with the exception
833
+ of `content` & `text` will be methods instead of properties. In the
834
+ async client, all methods will be async.
835
+
836
+ A migration script will be provided & the migration in general should
837
+ be smooth.
838
+
839
+ #### `.with_streaming_response`
840
+
841
+ The above interface eagerly reads the full response body when you make the request, which may not always be what you want.
842
+
843
+ To 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.
844
+
845
+ As 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.
846
+
847
+ ```python
848
+ with client.chat.completions.with_streaming_response.create(
849
+ messages=[
850
+ {
851
+ "role": "user",
852
+ "content": "Say this is a test",
853
+ }
854
+ ],
855
+ model="gpt-5.2",
856
+ ) as response:
857
+ print(response.headers.get("X-My-Header"))
858
+
859
+ for line in response.iter_lines():
860
+ print(line)
861
+ ```
862
+
863
+ The context manager is required so that the response will reliably be closed.
864
+
865
+ ### Making custom/undocumented requests
866
+
867
+ This library is typed for convenient access to the documented API.
868
+
869
+ If you need to access undocumented endpoints, params, or response properties, the library can still be used.
870
+
871
+ #### Undocumented endpoints
872
+
873
+ To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other
874
+ http verbs. Options on the client will be respected (such as retries) when making this request.
875
+
876
+ ```py
877
+ import httpx
878
+
879
+ response = client.post(
880
+ "/foo",
881
+ cast_to=httpx.Response,
882
+ body={"my_param": True},
883
+ )
884
+
885
+ print(response.headers.get("x-foo"))
886
+ ```
887
+
888
+ #### Undocumented request params
889
+
890
+ If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request
891
+ options.
892
+
893
+ #### Undocumented response properties
894
+
895
+ To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You
896
+ can also get all the extra fields on the Pydantic model as a dict with
897
+ [`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra).
898
+
899
+ ### Configuring the HTTP client
900
+
901
+ You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including:
902
+
903
+ - Support for [proxies](https://www.python-httpx.org/advanced/proxies/)
904
+ - Custom [transports](https://www.python-httpx.org/advanced/transports/)
905
+ - Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality
906
+
907
+ ```python
908
+ import httpx
909
+ from openai import OpenAI, DefaultHttpxClient
910
+
911
+ client = OpenAI(
912
+ # Or use the `OPENAI_BASE_URL` env var
913
+ base_url="http://my.test.server.example.com:8083/v1",
914
+ http_client=DefaultHttpxClient(
915
+ proxy="http://my.test.proxy.example.com",
916
+ transport=httpx.HTTPTransport(local_address="0.0.0.0"),
917
+ ),
918
+ )
919
+ ```
920
+
921
+ You can also customize the client on a per-request basis by using `with_options()`:
922
+
923
+ ```python
924
+ client.with_options(http_client=DefaultHttpxClient(...))
925
+ ```
926
+
927
+ ### Managing HTTP resources
928
+
929
+ By 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.
930
+
931
+ ```py
932
+ from openai import OpenAI
933
+
934
+ with OpenAI() as client:
935
+ # make requests here
936
+ ...
937
+
938
+ # HTTP client is now closed
939
+ ```
940
+
941
+ ## Microsoft Azure OpenAI
942
+
943
+ To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the `AzureOpenAI`
944
+ class instead of the `OpenAI` class.
945
+
946
+ > [!IMPORTANT]
947
+ > The Azure API shape differs from the core API shape which means that the static types for responses / params
948
+ > won't always be correct.
949
+
950
+ ```py
951
+ from openai import AzureOpenAI
952
+
953
+ # gets the API Key from environment variable AZURE_OPENAI_API_KEY
954
+ client = AzureOpenAI(
955
+ # https://learn.microsoft.com/azure/ai-services/openai/reference#rest-api-versioning
956
+ api_version="2023-07-01-preview",
957
+ # https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource
958
+ azure_endpoint="https://example-endpoint.openai.azure.com",
959
+ )
960
+
961
+ completion = client.chat.completions.create(
962
+ model="deployment-name", # e.g. gpt-35-instant
963
+ messages=[
964
+ {
965
+ "role": "user",
966
+ "content": "How do I output all files in a directory using Python?",
967
+ },
968
+ ],
969
+ )
970
+ print(completion.to_json())
971
+ ```
972
+
973
+ In addition to the options provided in the base `OpenAI` client, the following options are provided:
974
+
975
+ - `azure_endpoint` (or the `AZURE_OPENAI_ENDPOINT` environment variable)
976
+ - `azure_deployment`
977
+ - `api_version` (or the `OPENAI_API_VERSION` environment variable)
978
+ - `azure_ad_token` (or the `AZURE_OPENAI_AD_TOKEN` environment variable)
979
+ - `azure_ad_token_provider`
980
+
981
+ An 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).
982
+
983
+ ## Versioning
984
+
985
+ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
986
+
987
+ 1. Changes that only affect static types, without breaking runtime behavior.
988
+ 2. 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.)_
989
+ 3. Changes that we do not expect to impact the vast majority of users in practice.
990
+
991
+ We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
992
+
993
+ We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-python/issues) with questions, bugs, or suggestions.
994
+
995
+ ### Determining the installed version
996
+
997
+ If 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.
998
+
999
+ You can determine the version that is being used at runtime with:
1000
+
1001
+ ```py
1002
+ import openai
1003
+ print(openai.__version__)
1004
+ ```
1005
+
1006
+ ## Requirements
1007
+
1008
+ Python 3.9 or higher.
1009
+
1010
+ ## Contributing
1011
+
1012
+ See [the contributing documentation](https://github.com/openai/openai-python/tree/main/./CONTRIBUTING.md).
.venv311/Lib/site-packages/openai-2.36.0.dist-info/RECORD ADDED
The diff for this file is too large to render. See raw diff
 
.venv311/Lib/site-packages/openai-2.36.0.dist-info/REQUESTED ADDED
File without changes
.venv311/Lib/site-packages/openai-2.36.0.dist-info/WHEEL ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.26.3
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
.venv311/Lib/site-packages/openai-2.36.0.dist-info/licenses/LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2026 OpenAI
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
.venv311/Lib/site-packages/openai/_extras/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .numpy_proxy import numpy as numpy, has_numpy as has_numpy
2
+ from .pandas_proxy import pandas as pandas
3
+ from .sounddevice_proxy import sounddevice as sounddevice
.venv311/Lib/site-packages/openai/_extras/_common.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .._exceptions import OpenAIError
2
+
3
+ INSTRUCTIONS = """
4
+
5
+ OpenAI error:
6
+
7
+ missing `{library}`
8
+
9
+ This feature requires additional dependencies:
10
+
11
+ $ pip install openai[{extra}]
12
+
13
+ """
14
+
15
+
16
+ def format_instructions(*, library: str, extra: str) -> str:
17
+ return INSTRUCTIONS.format(library=library, extra=extra)
18
+
19
+
20
+ class MissingDependencyError(OpenAIError):
21
+ pass
.venv311/Lib/site-packages/openai/_extras/numpy_proxy.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+ from typing_extensions import override
5
+
6
+ from .._utils import LazyProxy
7
+ from ._common import MissingDependencyError, format_instructions
8
+
9
+ if TYPE_CHECKING:
10
+ import numpy as numpy
11
+
12
+
13
+ NUMPY_INSTRUCTIONS = format_instructions(library="numpy", extra="voice_helpers")
14
+
15
+
16
+ class NumpyProxy(LazyProxy[Any]):
17
+ @override
18
+ def __load__(self) -> Any:
19
+ try:
20
+ import numpy
21
+ except ImportError as err:
22
+ raise MissingDependencyError(NUMPY_INSTRUCTIONS) from err
23
+
24
+ return numpy
25
+
26
+
27
+ if not TYPE_CHECKING:
28
+ numpy = NumpyProxy()
29
+
30
+
31
+ def has_numpy() -> bool:
32
+ try:
33
+ import numpy # noqa: F401 # pyright: ignore[reportUnusedImport]
34
+ except ImportError:
35
+ return False
36
+
37
+ return True
.venv311/Lib/site-packages/openai/_extras/pandas_proxy.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+ from typing_extensions import override
5
+
6
+ from .._utils import LazyProxy
7
+ from ._common import MissingDependencyError, format_instructions
8
+
9
+ if TYPE_CHECKING:
10
+ import pandas as pandas
11
+
12
+
13
+ PANDAS_INSTRUCTIONS = format_instructions(library="pandas", extra="datalib")
14
+
15
+
16
+ class PandasProxy(LazyProxy[Any]):
17
+ @override
18
+ def __load__(self) -> Any:
19
+ try:
20
+ import pandas
21
+ except ImportError as err:
22
+ raise MissingDependencyError(PANDAS_INSTRUCTIONS) from err
23
+
24
+ return pandas
25
+
26
+
27
+ if not TYPE_CHECKING:
28
+ pandas = PandasProxy()
.venv311/Lib/site-packages/openai/_extras/sounddevice_proxy.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+ from typing_extensions import override
5
+
6
+ from .._utils import LazyProxy
7
+ from ._common import MissingDependencyError, format_instructions
8
+
9
+ if TYPE_CHECKING:
10
+ import sounddevice as sounddevice # type: ignore
11
+
12
+
13
+ SOUNDDEVICE_INSTRUCTIONS = format_instructions(library="sounddevice", extra="voice_helpers")
14
+
15
+
16
+ class SounddeviceProxy(LazyProxy[Any]):
17
+ @override
18
+ def __load__(self) -> Any:
19
+ try:
20
+ import sounddevice # type: ignore
21
+ except ImportError as err:
22
+ raise MissingDependencyError(SOUNDDEVICE_INSTRUCTIONS) from err
23
+
24
+ return sounddevice
25
+
26
+
27
+ if not TYPE_CHECKING:
28
+ sounddevice = SounddeviceProxy()
.venv311/Lib/site-packages/openai/_utils/__init__.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ._logs import SensitiveHeadersFilter as SensitiveHeadersFilter
2
+ from ._path import path_template as path_template
3
+ from ._sync import asyncify as asyncify
4
+ from ._proxy import LazyProxy as LazyProxy
5
+ from ._utils import (
6
+ flatten as flatten,
7
+ is_dict as is_dict,
8
+ is_list as is_list,
9
+ is_given as is_given,
10
+ is_tuple as is_tuple,
11
+ json_safe as json_safe,
12
+ lru_cache as lru_cache,
13
+ is_mapping as is_mapping,
14
+ is_tuple_t as is_tuple_t,
15
+ is_iterable as is_iterable,
16
+ is_sequence as is_sequence,
17
+ coerce_float as coerce_float,
18
+ is_mapping_t as is_mapping_t,
19
+ removeprefix as removeprefix,
20
+ removesuffix as removesuffix,
21
+ extract_files as extract_files,
22
+ is_sequence_t as is_sequence_t,
23
+ required_args as required_args,
24
+ coerce_boolean as coerce_boolean,
25
+ coerce_integer as coerce_integer,
26
+ file_from_path as file_from_path,
27
+ is_azure_client as is_azure_client,
28
+ strip_not_given as strip_not_given,
29
+ get_async_library as get_async_library,
30
+ maybe_coerce_float as maybe_coerce_float,
31
+ get_required_header as get_required_header,
32
+ maybe_coerce_boolean as maybe_coerce_boolean,
33
+ maybe_coerce_integer as maybe_coerce_integer,
34
+ is_async_azure_client as is_async_azure_client,
35
+ )
36
+ from ._compat import (
37
+ get_args as get_args,
38
+ is_union as is_union,
39
+ get_origin as get_origin,
40
+ is_typeddict as is_typeddict,
41
+ is_literal_type as is_literal_type,
42
+ )
43
+ from ._typing import (
44
+ is_list_type as is_list_type,
45
+ is_union_type as is_union_type,
46
+ extract_type_arg as extract_type_arg,
47
+ is_iterable_type as is_iterable_type,
48
+ is_required_type as is_required_type,
49
+ is_sequence_type as is_sequence_type,
50
+ is_annotated_type as is_annotated_type,
51
+ is_type_alias_type as is_type_alias_type,
52
+ strip_annotated_type as strip_annotated_type,
53
+ extract_type_var_from_base as extract_type_var_from_base,
54
+ )
55
+ from ._streams import consume_sync_iterator as consume_sync_iterator, consume_async_iterator as consume_async_iterator
56
+ from ._transform import (
57
+ PropertyInfo as PropertyInfo,
58
+ transform as transform,
59
+ async_transform as async_transform,
60
+ maybe_transform as maybe_transform,
61
+ async_maybe_transform as async_maybe_transform,
62
+ )
63
+ from ._reflection import (
64
+ function_has_argument as function_has_argument,
65
+ assert_signatures_in_sync as assert_signatures_in_sync,
66
+ )
67
+ from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime
.venv311/Lib/site-packages/openai/_utils/_compat.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ import typing_extensions
5
+ from typing import Any, Type, Union, Literal, Optional
6
+ from datetime import date, datetime
7
+ from typing_extensions import get_args as _get_args, get_origin as _get_origin
8
+
9
+ from .._types import StrBytesIntFloat
10
+ from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime
11
+
12
+ _LITERAL_TYPES = {Literal, typing_extensions.Literal}
13
+
14
+
15
+ def get_args(tp: type[Any]) -> tuple[Any, ...]:
16
+ return _get_args(tp)
17
+
18
+
19
+ def get_origin(tp: type[Any]) -> type[Any] | None:
20
+ return _get_origin(tp)
21
+
22
+
23
+ def is_union(tp: Optional[Type[Any]]) -> bool:
24
+ if sys.version_info < (3, 10):
25
+ return tp is Union # type: ignore[comparison-overlap]
26
+ else:
27
+ import types
28
+
29
+ return tp is Union or tp is types.UnionType # type: ignore[comparison-overlap]
30
+
31
+
32
+ def is_typeddict(tp: Type[Any]) -> bool:
33
+ return typing_extensions.is_typeddict(tp)
34
+
35
+
36
+ def is_literal_type(tp: Type[Any]) -> bool:
37
+ return get_origin(tp) in _LITERAL_TYPES
38
+
39
+
40
+ def parse_date(value: Union[date, StrBytesIntFloat]) -> date:
41
+ return _parse_date(value)
42
+
43
+
44
+ def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime:
45
+ return _parse_datetime(value)
.venv311/Lib/site-packages/openai/_utils/_datetime_parse.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py
3
+ without the Pydantic v1 specific errors.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import re
9
+ from typing import Dict, Union, Optional
10
+ from datetime import date, datetime, timezone, timedelta
11
+
12
+ from .._types import StrBytesIntFloat
13
+
14
+ date_expr = r"(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})"
15
+ time_expr = (
16
+ r"(?P<hour>\d{1,2}):(?P<minute>\d{1,2})"
17
+ r"(?::(?P<second>\d{1,2})(?:\.(?P<microsecond>\d{1,6})\d{0,6})?)?"
18
+ r"(?P<tzinfo>Z|[+-]\d{2}(?::?\d{2})?)?$"
19
+ )
20
+
21
+ date_re = re.compile(f"{date_expr}$")
22
+ datetime_re = re.compile(f"{date_expr}[T ]{time_expr}")
23
+
24
+
25
+ EPOCH = datetime(1970, 1, 1)
26
+ # if greater than this, the number is in ms, if less than or equal it's in seconds
27
+ # (in seconds this is 11th October 2603, in ms it's 20th August 1970)
28
+ MS_WATERSHED = int(2e10)
29
+ # slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9
30
+ MAX_NUMBER = int(3e20)
31
+
32
+
33
+ def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]:
34
+ if isinstance(value, (int, float)):
35
+ return value
36
+ try:
37
+ return float(value)
38
+ except ValueError:
39
+ return None
40
+ except TypeError:
41
+ raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None
42
+
43
+
44
+ def _from_unix_seconds(seconds: Union[int, float]) -> datetime:
45
+ if seconds > MAX_NUMBER:
46
+ return datetime.max
47
+ elif seconds < -MAX_NUMBER:
48
+ return datetime.min
49
+
50
+ while abs(seconds) > MS_WATERSHED:
51
+ seconds /= 1000
52
+ dt = EPOCH + timedelta(seconds=seconds)
53
+ return dt.replace(tzinfo=timezone.utc)
54
+
55
+
56
+ def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]:
57
+ if value == "Z":
58
+ return timezone.utc
59
+ elif value is not None:
60
+ offset_mins = int(value[-2:]) if len(value) > 3 else 0
61
+ offset = 60 * int(value[1:3]) + offset_mins
62
+ if value[0] == "-":
63
+ offset = -offset
64
+ return timezone(timedelta(minutes=offset))
65
+ else:
66
+ return None
67
+
68
+
69
+ def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime:
70
+ """
71
+ Parse a datetime/int/float/string and return a datetime.datetime.
72
+
73
+ This function supports time zone offsets. When the input contains one,
74
+ the output uses a timezone with a fixed offset from UTC.
75
+
76
+ Raise ValueError if the input is well formatted but not a valid datetime.
77
+ Raise ValueError if the input isn't well formatted.
78
+ """
79
+ if isinstance(value, datetime):
80
+ return value
81
+
82
+ number = _get_numeric(value, "datetime")
83
+ if number is not None:
84
+ return _from_unix_seconds(number)
85
+
86
+ if isinstance(value, bytes):
87
+ value = value.decode()
88
+
89
+ assert not isinstance(value, (float, int))
90
+
91
+ match = datetime_re.match(value)
92
+ if match is None:
93
+ raise ValueError("invalid datetime format")
94
+
95
+ kw = match.groupdict()
96
+ if kw["microsecond"]:
97
+ kw["microsecond"] = kw["microsecond"].ljust(6, "0")
98
+
99
+ tzinfo = _parse_timezone(kw.pop("tzinfo"))
100
+ kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None}
101
+ kw_["tzinfo"] = tzinfo
102
+
103
+ return datetime(**kw_) # type: ignore
104
+
105
+
106
+ def parse_date(value: Union[date, StrBytesIntFloat]) -> date:
107
+ """
108
+ Parse a date/int/float/string and return a datetime.date.
109
+
110
+ Raise ValueError if the input is well formatted but not a valid date.
111
+ Raise ValueError if the input isn't well formatted.
112
+ """
113
+ if isinstance(value, date):
114
+ if isinstance(value, datetime):
115
+ return value.date()
116
+ else:
117
+ return value
118
+
119
+ number = _get_numeric(value, "date")
120
+ if number is not None:
121
+ return _from_unix_seconds(number).date()
122
+
123
+ if isinstance(value, bytes):
124
+ value = value.decode()
125
+
126
+ assert not isinstance(value, (float, int))
127
+ match = date_re.match(value)
128
+ if match is None:
129
+ raise ValueError("invalid date format")
130
+
131
+ kw = {k: int(v) for k, v in match.groupdict().items()}
132
+
133
+ try:
134
+ return date(**kw)
135
+ except ValueError:
136
+ raise ValueError("invalid date format") from None
.venv311/Lib/site-packages/openai/_utils/_json.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from typing import Any
3
+ from datetime import datetime
4
+ from typing_extensions import override
5
+
6
+ import pydantic
7
+
8
+ from .._compat import model_dump
9
+
10
+
11
+ def openapi_dumps(obj: Any) -> bytes:
12
+ """
13
+ Serialize an object to UTF-8 encoded JSON bytes.
14
+
15
+ Extends the standard json.dumps with support for additional types
16
+ commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc.
17
+ """
18
+ return json.dumps(
19
+ obj,
20
+ cls=_CustomEncoder,
21
+ # Uses the same defaults as httpx's JSON serialization
22
+ ensure_ascii=False,
23
+ separators=(",", ":"),
24
+ allow_nan=False,
25
+ ).encode()
26
+
27
+
28
+ class _CustomEncoder(json.JSONEncoder):
29
+ @override
30
+ def default(self, o: Any) -> Any:
31
+ if isinstance(o, datetime):
32
+ return o.isoformat()
33
+ if isinstance(o, pydantic.BaseModel):
34
+ return model_dump(o, exclude_unset=True, mode="json", by_alias=True)
35
+ return super().default(o)
.venv311/Lib/site-packages/openai/_utils/_logs.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import logging
3
+ from typing_extensions import override
4
+
5
+ from ._utils import is_dict
6
+
7
+ logger: logging.Logger = logging.getLogger("openai")
8
+ httpx_logger: logging.Logger = logging.getLogger("httpx")
9
+
10
+
11
+ SENSITIVE_HEADERS = {"api-key", "authorization"}
12
+
13
+
14
+ def _basic_config() -> None:
15
+ # e.g. [2023-10-05 14:12:26 - openai._base_client:818 - DEBUG] HTTP Request: POST http://127.0.0.1:4010/foo/bar "200 OK"
16
+ logging.basicConfig(
17
+ format="[%(asctime)s - %(name)s:%(lineno)d - %(levelname)s] %(message)s",
18
+ datefmt="%Y-%m-%d %H:%M:%S",
19
+ )
20
+
21
+
22
+ def setup_logging() -> None:
23
+ env = os.environ.get("OPENAI_LOG")
24
+ if env == "debug":
25
+ _basic_config()
26
+ logger.setLevel(logging.DEBUG)
27
+ httpx_logger.setLevel(logging.DEBUG)
28
+ elif env == "info":
29
+ _basic_config()
30
+ logger.setLevel(logging.INFO)
31
+ httpx_logger.setLevel(logging.INFO)
32
+
33
+
34
+ class SensitiveHeadersFilter(logging.Filter):
35
+ @override
36
+ def filter(self, record: logging.LogRecord) -> bool:
37
+ if is_dict(record.args) and "headers" in record.args and is_dict(record.args["headers"]):
38
+ headers = record.args["headers"] = {**record.args["headers"]}
39
+ for header in headers:
40
+ if str(header).lower() in SENSITIVE_HEADERS:
41
+ headers[header] = "<redacted>"
42
+ return True
.venv311/Lib/site-packages/openai/_utils/_path.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import (
5
+ Any,
6
+ Mapping,
7
+ Callable,
8
+ )
9
+ from urllib.parse import quote
10
+
11
+ # Matches '.' or '..' where each dot is either literal or percent-encoded (%2e / %2E).
12
+ _DOT_SEGMENT_RE = re.compile(r"^(?:\.|%2[eE]){1,2}$")
13
+
14
+ _PLACEHOLDER_RE = re.compile(r"\{(\w+)\}")
15
+
16
+
17
+ def _quote_path_segment_part(value: str) -> str:
18
+ """Percent-encode `value` for use in a URI path segment.
19
+
20
+ Considers characters not in `pchar` set from RFC 3986 §3.3 to be unsafe.
21
+ https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
22
+ """
23
+ # quote() already treats unreserved characters (letters, digits, and -._~)
24
+ # as safe, so we only need to add sub-delims, ':', and '@'.
25
+ # Notably, unlike the default `safe` for quote(), / is unsafe and must be quoted.
26
+ return quote(value, safe="!$&'()*+,;=:@")
27
+
28
+
29
+ def _quote_query_part(value: str) -> str:
30
+ """Percent-encode `value` for use in a URI query string.
31
+
32
+ Considers &, = and characters not in `query` set from RFC 3986 §3.4 to be unsafe.
33
+ https://datatracker.ietf.org/doc/html/rfc3986#section-3.4
34
+ """
35
+ return quote(value, safe="!$'()*+,;:@/?")
36
+
37
+
38
+ def _quote_fragment_part(value: str) -> str:
39
+ """Percent-encode `value` for use in a URI fragment.
40
+
41
+ Considers characters not in `fragment` set from RFC 3986 §3.5 to be unsafe.
42
+ https://datatracker.ietf.org/doc/html/rfc3986#section-3.5
43
+ """
44
+ return quote(value, safe="!$&'()*+,;=:@/?")
45
+
46
+
47
+ def _interpolate(
48
+ template: str,
49
+ values: Mapping[str, Any],
50
+ quoter: Callable[[str], str],
51
+ ) -> str:
52
+ """Replace {name} placeholders in `template`, quoting each value with `quoter`.
53
+
54
+ Placeholder names are looked up in `values`.
55
+
56
+ Raises:
57
+ KeyError: If a placeholder is not found in `values`.
58
+ """
59
+ # re.split with a capturing group returns alternating
60
+ # [text, name, text, name, ..., text] elements.
61
+ parts = _PLACEHOLDER_RE.split(template)
62
+
63
+ for i in range(1, len(parts), 2):
64
+ name = parts[i]
65
+ if name not in values:
66
+ raise KeyError(f"a value for placeholder {{{name}}} was not provided")
67
+ val = values[name]
68
+ if val is None:
69
+ parts[i] = "null"
70
+ elif isinstance(val, bool):
71
+ parts[i] = "true" if val else "false"
72
+ else:
73
+ parts[i] = quoter(str(values[name]))
74
+
75
+ return "".join(parts)
76
+
77
+
78
+ def path_template(template: str, /, **kwargs: Any) -> str:
79
+ """Interpolate {name} placeholders in `template` from keyword arguments.
80
+
81
+ Args:
82
+ template: The template string containing {name} placeholders.
83
+ **kwargs: Keyword arguments to interpolate into the template.
84
+
85
+ Returns:
86
+ The template with placeholders interpolated and percent-encoded.
87
+
88
+ Safe characters for percent-encoding are dependent on the URI component.
89
+ Placeholders in path and fragment portions are percent-encoded where the `segment`
90
+ and `fragment` sets from RFC 3986 respectively are considered safe.
91
+ Placeholders in the query portion are percent-encoded where the `query` set from
92
+ RFC 3986 §3.3 is considered safe except for = and & characters.
93
+
94
+ Raises:
95
+ KeyError: If a placeholder is not found in `kwargs`.
96
+ ValueError: If resulting path contains /./ or /../ segments (including percent-encoded dot-segments).
97
+ """
98
+ # Split the template into path, query, and fragment portions.
99
+ fragment_template: str | None = None
100
+ query_template: str | None = None
101
+
102
+ rest = template
103
+ if "#" in rest:
104
+ rest, fragment_template = rest.split("#", 1)
105
+ if "?" in rest:
106
+ rest, query_template = rest.split("?", 1)
107
+ path_template = rest
108
+
109
+ # Interpolate each portion with the appropriate quoting rules.
110
+ path_result = _interpolate(path_template, kwargs, _quote_path_segment_part)
111
+
112
+ # Reject dot-segments (. and ..) in the final assembled path. The check
113
+ # runs after interpolation so that adjacent placeholders or a mix of static
114
+ # text and placeholders that together form a dot-segment are caught.
115
+ # Also reject percent-encoded dot-segments to protect against incorrectly
116
+ # implemented normalization in servers/proxies.
117
+ for segment in path_result.split("/"):
118
+ if _DOT_SEGMENT_RE.match(segment):
119
+ raise ValueError(f"Constructed path {path_result!r} contains dot-segment {segment!r} which is not allowed")
120
+
121
+ result = path_result
122
+ if query_template is not None:
123
+ result += "?" + _interpolate(query_template, kwargs, _quote_query_part)
124
+ if fragment_template is not None:
125
+ result += "#" + _interpolate(fragment_template, kwargs, _quote_fragment_part)
126
+
127
+ return result
.venv311/Lib/site-packages/openai/_utils/_proxy.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Generic, TypeVar, Iterable, cast
5
+ from typing_extensions import override
6
+
7
+ T = TypeVar("T")
8
+
9
+
10
+ class LazyProxy(Generic[T], ABC):
11
+ """Implements data methods to pretend that an instance is another instance.
12
+
13
+ This includes forwarding attribute access and other methods.
14
+ """
15
+
16
+ # Note: we have to special case proxies that themselves return proxies
17
+ # to support using a proxy as a catch-all for any random access, e.g. `proxy.foo.bar.baz`
18
+
19
+ def __getattr__(self, attr: str) -> object:
20
+ proxied = self.__get_proxied__()
21
+ if isinstance(proxied, LazyProxy):
22
+ return proxied # pyright: ignore
23
+ return getattr(proxied, attr)
24
+
25
+ @override
26
+ def __repr__(self) -> str:
27
+ proxied = self.__get_proxied__()
28
+ if isinstance(proxied, LazyProxy):
29
+ return proxied.__class__.__name__
30
+ return repr(self.__get_proxied__())
31
+
32
+ @override
33
+ def __str__(self) -> str:
34
+ proxied = self.__get_proxied__()
35
+ if isinstance(proxied, LazyProxy):
36
+ return proxied.__class__.__name__
37
+ return str(proxied)
38
+
39
+ @override
40
+ def __dir__(self) -> Iterable[str]:
41
+ proxied = self.__get_proxied__()
42
+ if isinstance(proxied, LazyProxy):
43
+ return []
44
+ return proxied.__dir__()
45
+
46
+ @property # type: ignore
47
+ @override
48
+ def __class__(self) -> type: # pyright: ignore
49
+ try:
50
+ proxied = self.__get_proxied__()
51
+ except Exception:
52
+ return type(self)
53
+ if issubclass(type(proxied), LazyProxy):
54
+ return type(proxied)
55
+ return proxied.__class__
56
+
57
+ def __get_proxied__(self) -> T:
58
+ return self.__load__()
59
+
60
+ def __as_proxied__(self) -> T:
61
+ """Helper method that returns the current proxy, typed as the loaded object"""
62
+ return cast(T, self)
63
+
64
+ @abstractmethod
65
+ def __load__(self) -> T: ...
.venv311/Lib/site-packages/openai/_utils/_reflection.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ from typing import Any, Callable
5
+
6
+
7
+ def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool:
8
+ """Returns whether or not the given function has a specific parameter"""
9
+ sig = inspect.signature(func)
10
+ return arg_name in sig.parameters
11
+
12
+
13
+ def assert_signatures_in_sync(
14
+ source_func: Callable[..., Any],
15
+ check_func: Callable[..., Any],
16
+ *,
17
+ exclude_params: set[str] = set(),
18
+ description: str = "",
19
+ ) -> None:
20
+ """Ensure that the signature of the second function matches the first."""
21
+
22
+ check_sig = inspect.signature(check_func)
23
+ source_sig = inspect.signature(source_func)
24
+
25
+ errors: list[str] = []
26
+
27
+ for name, source_param in source_sig.parameters.items():
28
+ if name in exclude_params:
29
+ continue
30
+
31
+ custom_param = check_sig.parameters.get(name)
32
+ if not custom_param:
33
+ errors.append(f"the `{name}` param is missing")
34
+ continue
35
+
36
+ if custom_param.annotation != source_param.annotation:
37
+ errors.append(
38
+ f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}"
39
+ )
40
+ continue
41
+
42
+ if errors:
43
+ raise AssertionError(
44
+ f"{len(errors)} errors encountered when comparing signatures{description}:\n\n" + "\n\n".join(errors)
45
+ )
.venv311/Lib/site-packages/openai/_utils/_resources_proxy.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+ from typing_extensions import override
5
+
6
+ from ._proxy import LazyProxy
7
+
8
+
9
+ class ResourcesProxy(LazyProxy[Any]):
10
+ """A proxy for the `openai.resources` module.
11
+
12
+ This is used so that we can lazily import `openai.resources` only when
13
+ needed *and* so that users can just import `openai` and reference `openai.resources`
14
+ """
15
+
16
+ @override
17
+ def __load__(self) -> Any:
18
+ import importlib
19
+
20
+ mod = importlib.import_module("openai.resources")
21
+ return mod
22
+
23
+
24
+ resources = ResourcesProxy().__as_proxied__()
.venv311/Lib/site-packages/openai/_utils/_streams.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any
2
+ from typing_extensions import Iterator, AsyncIterator
3
+
4
+
5
+ def consume_sync_iterator(iterator: Iterator[Any]) -> None:
6
+ for _ in iterator:
7
+ ...
8
+
9
+
10
+ async def consume_async_iterator(iterator: AsyncIterator[Any]) -> None:
11
+ async for _ in iterator:
12
+ ...
.venv311/Lib/site-packages/openai/_utils/_sync.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import functools
5
+ from typing import TypeVar, Callable, Awaitable
6
+ from typing_extensions import ParamSpec
7
+
8
+ import anyio
9
+ import sniffio
10
+ import anyio.to_thread
11
+
12
+ T_Retval = TypeVar("T_Retval")
13
+ T_ParamSpec = ParamSpec("T_ParamSpec")
14
+
15
+
16
+ async def to_thread(
17
+ func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
18
+ ) -> T_Retval:
19
+ if sniffio.current_async_library() == "asyncio":
20
+ return await asyncio.to_thread(func, *args, **kwargs)
21
+
22
+ return await anyio.to_thread.run_sync(
23
+ functools.partial(func, *args, **kwargs),
24
+ )
25
+
26
+
27
+ # inspired by `asyncer`, https://github.com/tiangolo/asyncer
28
+ def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]:
29
+ """
30
+ Take a blocking function and create an async one that receives the same
31
+ positional and keyword arguments.
32
+
33
+ Usage:
34
+
35
+ ```python
36
+ def blocking_func(arg1, arg2, kwarg1=None):
37
+ # blocking code
38
+ return result
39
+
40
+
41
+ result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1)
42
+ ```
43
+
44
+ ## Arguments
45
+
46
+ `function`: a blocking regular callable (e.g. a function)
47
+
48
+ ## Return
49
+
50
+ An async function that takes the same positional and keyword arguments as the
51
+ original one, that when called runs the same original function in a thread worker
52
+ and returns the result.
53
+ """
54
+
55
+ async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval:
56
+ return await to_thread(function, *args, **kwargs)
57
+
58
+ return wrapper
.venv311/Lib/site-packages/openai/_utils/_transform.py ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import io
4
+ import base64
5
+ import pathlib
6
+ from typing import Any, Mapping, TypeVar, cast
7
+ from datetime import date, datetime
8
+ from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints
9
+
10
+ import anyio
11
+ import pydantic
12
+
13
+ from ._utils import (
14
+ is_list,
15
+ is_given,
16
+ lru_cache,
17
+ is_mapping,
18
+ is_iterable,
19
+ is_sequence,
20
+ )
21
+ from .._files import is_base64_file_input
22
+ from ._compat import get_origin, is_typeddict
23
+ from ._typing import (
24
+ is_list_type,
25
+ is_union_type,
26
+ extract_type_arg,
27
+ is_iterable_type,
28
+ is_required_type,
29
+ is_sequence_type,
30
+ is_annotated_type,
31
+ strip_annotated_type,
32
+ )
33
+
34
+ _T = TypeVar("_T")
35
+
36
+
37
+ # TODO: support for drilling globals() and locals()
38
+ # TODO: ensure works correctly with forward references in all cases
39
+
40
+
41
+ PropertyFormat = Literal["iso8601", "base64", "custom"]
42
+
43
+
44
+ class PropertyInfo:
45
+ """Metadata class to be used in Annotated types to provide information about a given type.
46
+
47
+ For example:
48
+
49
+ class MyParams(TypedDict):
50
+ account_holder_name: Annotated[str, PropertyInfo(alias='accountHolderName')]
51
+
52
+ This means that {'account_holder_name': 'Robert'} will be transformed to {'accountHolderName': 'Robert'} before being sent to the API.
53
+ """
54
+
55
+ alias: str | None
56
+ format: PropertyFormat | None
57
+ format_template: str | None
58
+ discriminator: str | None
59
+
60
+ def __init__(
61
+ self,
62
+ *,
63
+ alias: str | None = None,
64
+ format: PropertyFormat | None = None,
65
+ format_template: str | None = None,
66
+ discriminator: str | None = None,
67
+ ) -> None:
68
+ self.alias = alias
69
+ self.format = format
70
+ self.format_template = format_template
71
+ self.discriminator = discriminator
72
+
73
+ @override
74
+ def __repr__(self) -> str:
75
+ return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')"
76
+
77
+
78
+ def maybe_transform(
79
+ data: object,
80
+ expected_type: object,
81
+ ) -> Any | None:
82
+ """Wrapper over `transform()` that allows `None` to be passed.
83
+
84
+ See `transform()` for more details.
85
+ """
86
+ if data is None:
87
+ return None
88
+ return transform(data, expected_type)
89
+
90
+
91
+ # Wrapper over _transform_recursive providing fake types
92
+ def transform(
93
+ data: _T,
94
+ expected_type: object,
95
+ ) -> _T:
96
+ """Transform dictionaries based off of type information from the given type, for example:
97
+
98
+ ```py
99
+ class Params(TypedDict, total=False):
100
+ card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]]
101
+
102
+
103
+ transformed = transform({"card_id": "<my card ID>"}, Params)
104
+ # {'cardID': '<my card ID>'}
105
+ ```
106
+
107
+ Any keys / data that does not have type information given will be included as is.
108
+
109
+ It should be noted that the transformations that this function does are not represented in the type system.
110
+ """
111
+ transformed = _transform_recursive(data, annotation=cast(type, expected_type))
112
+ return cast(_T, transformed)
113
+
114
+
115
+ @lru_cache(maxsize=8096)
116
+ def _get_annotated_type(type_: type) -> type | None:
117
+ """If the given type is an `Annotated` type then it is returned, if not `None` is returned.
118
+
119
+ This also unwraps the type when applicable, e.g. `Required[Annotated[T, ...]]`
120
+ """
121
+ if is_required_type(type_):
122
+ # Unwrap `Required[Annotated[T, ...]]` to `Annotated[T, ...]`
123
+ type_ = get_args(type_)[0]
124
+
125
+ if is_annotated_type(type_):
126
+ return type_
127
+
128
+ return None
129
+
130
+
131
+ def _maybe_transform_key(key: str, type_: type) -> str:
132
+ """Transform the given `data` based on the annotations provided in `type_`.
133
+
134
+ Note: this function only looks at `Annotated` types that contain `PropertyInfo` metadata.
135
+ """
136
+ annotated_type = _get_annotated_type(type_)
137
+ if annotated_type is None:
138
+ # no `Annotated` definition for this type, no transformation needed
139
+ return key
140
+
141
+ # ignore the first argument as it is the actual type
142
+ annotations = get_args(annotated_type)[1:]
143
+ for annotation in annotations:
144
+ if isinstance(annotation, PropertyInfo) and annotation.alias is not None:
145
+ return annotation.alias
146
+
147
+ return key
148
+
149
+
150
+ def _no_transform_needed(annotation: type) -> bool:
151
+ return annotation == float or annotation == int
152
+
153
+
154
+ def _transform_recursive(
155
+ data: object,
156
+ *,
157
+ annotation: type,
158
+ inner_type: type | None = None,
159
+ ) -> object:
160
+ """Transform the given data against the expected type.
161
+
162
+ Args:
163
+ annotation: The direct type annotation given to the particular piece of data.
164
+ This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc
165
+
166
+ inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type
167
+ is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in
168
+ the list can be transformed using the metadata from the container type.
169
+
170
+ Defaults to the same value as the `annotation` argument.
171
+ """
172
+ from .._compat import model_dump
173
+
174
+ if inner_type is None:
175
+ inner_type = annotation
176
+
177
+ stripped_type = strip_annotated_type(inner_type)
178
+ origin = get_origin(stripped_type) or stripped_type
179
+ if is_typeddict(stripped_type) and is_mapping(data):
180
+ return _transform_typeddict(data, stripped_type)
181
+
182
+ if origin == dict and is_mapping(data):
183
+ items_type = get_args(stripped_type)[1]
184
+ return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()}
185
+
186
+ if (
187
+ # List[T]
188
+ (is_list_type(stripped_type) and is_list(data))
189
+ # Iterable[T]
190
+ or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
191
+ # Sequence[T]
192
+ or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str))
193
+ ):
194
+ # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually
195
+ # intended as an iterable, so we don't transform it.
196
+ if isinstance(data, dict):
197
+ return cast(object, data)
198
+
199
+ inner_type = extract_type_arg(stripped_type, 0)
200
+ if _no_transform_needed(inner_type):
201
+ # for some types there is no need to transform anything, so we can get a small
202
+ # perf boost from skipping that work.
203
+ #
204
+ # but we still need to convert to a list to ensure the data is json-serializable
205
+ if is_list(data):
206
+ return data
207
+ return list(data)
208
+
209
+ return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]
210
+
211
+ if is_union_type(stripped_type):
212
+ # For union types we run the transformation against all subtypes to ensure that everything is transformed.
213
+ #
214
+ # TODO: there may be edge cases where the same normalized field name will transform to two different names
215
+ # in different subtypes.
216
+ for subtype in get_args(stripped_type):
217
+ data = _transform_recursive(data, annotation=annotation, inner_type=subtype)
218
+ return data
219
+
220
+ if isinstance(data, pydantic.BaseModel):
221
+ return model_dump(data, exclude_unset=True, mode="json", exclude=getattr(data, "__api_exclude__", None))
222
+
223
+ annotated_type = _get_annotated_type(annotation)
224
+ if annotated_type is None:
225
+ return data
226
+
227
+ # ignore the first argument as it is the actual type
228
+ annotations = get_args(annotated_type)[1:]
229
+ for annotation in annotations:
230
+ if isinstance(annotation, PropertyInfo) and annotation.format is not None:
231
+ return _format_data(data, annotation.format, annotation.format_template)
232
+
233
+ return data
234
+
235
+
236
+ def _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object:
237
+ if isinstance(data, (date, datetime)):
238
+ if format_ == "iso8601":
239
+ return data.isoformat()
240
+
241
+ if format_ == "custom" and format_template is not None:
242
+ return data.strftime(format_template)
243
+
244
+ if format_ == "base64" and is_base64_file_input(data):
245
+ binary: str | bytes | None = None
246
+
247
+ if isinstance(data, pathlib.Path):
248
+ binary = data.read_bytes()
249
+ elif isinstance(data, io.IOBase):
250
+ binary = data.read()
251
+
252
+ if isinstance(binary, str): # type: ignore[unreachable]
253
+ binary = binary.encode()
254
+
255
+ if not isinstance(binary, bytes):
256
+ raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}")
257
+
258
+ return base64.b64encode(binary).decode("ascii")
259
+
260
+ return data
261
+
262
+
263
+ def _transform_typeddict(
264
+ data: Mapping[str, object],
265
+ expected_type: type,
266
+ ) -> Mapping[str, object]:
267
+ result: dict[str, object] = {}
268
+ annotations = get_type_hints(expected_type, include_extras=True)
269
+ for key, value in data.items():
270
+ if not is_given(value):
271
+ # we don't need to include omitted values here as they'll
272
+ # be stripped out before the request is sent anyway
273
+ continue
274
+
275
+ type_ = annotations.get(key)
276
+ if type_ is None:
277
+ # we do not have a type annotation for this field, leave it as is
278
+ result[key] = value
279
+ else:
280
+ result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_)
281
+ return result
282
+
283
+
284
+ async def async_maybe_transform(
285
+ data: object,
286
+ expected_type: object,
287
+ ) -> Any | None:
288
+ """Wrapper over `async_transform()` that allows `None` to be passed.
289
+
290
+ See `async_transform()` for more details.
291
+ """
292
+ if data is None:
293
+ return None
294
+ return await async_transform(data, expected_type)
295
+
296
+
297
+ async def async_transform(
298
+ data: _T,
299
+ expected_type: object,
300
+ ) -> _T:
301
+ """Transform dictionaries based off of type information from the given type, for example:
302
+
303
+ ```py
304
+ class Params(TypedDict, total=False):
305
+ card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]]
306
+
307
+
308
+ transformed = transform({"card_id": "<my card ID>"}, Params)
309
+ # {'cardID': '<my card ID>'}
310
+ ```
311
+
312
+ Any keys / data that does not have type information given will be included as is.
313
+
314
+ It should be noted that the transformations that this function does are not represented in the type system.
315
+ """
316
+ transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type))
317
+ return cast(_T, transformed)
318
+
319
+
320
+ async def _async_transform_recursive(
321
+ data: object,
322
+ *,
323
+ annotation: type,
324
+ inner_type: type | None = None,
325
+ ) -> object:
326
+ """Transform the given data against the expected type.
327
+
328
+ Args:
329
+ annotation: The direct type annotation given to the particular piece of data.
330
+ This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc
331
+
332
+ inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type
333
+ is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in
334
+ the list can be transformed using the metadata from the container type.
335
+
336
+ Defaults to the same value as the `annotation` argument.
337
+ """
338
+ from .._compat import model_dump
339
+
340
+ if inner_type is None:
341
+ inner_type = annotation
342
+
343
+ stripped_type = strip_annotated_type(inner_type)
344
+ origin = get_origin(stripped_type) or stripped_type
345
+ if is_typeddict(stripped_type) and is_mapping(data):
346
+ return await _async_transform_typeddict(data, stripped_type)
347
+
348
+ if origin == dict and is_mapping(data):
349
+ items_type = get_args(stripped_type)[1]
350
+ return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()}
351
+
352
+ if (
353
+ # List[T]
354
+ (is_list_type(stripped_type) and is_list(data))
355
+ # Iterable[T]
356
+ or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
357
+ # Sequence[T]
358
+ or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str))
359
+ ):
360
+ # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually
361
+ # intended as an iterable, so we don't transform it.
362
+ if isinstance(data, dict):
363
+ return cast(object, data)
364
+
365
+ inner_type = extract_type_arg(stripped_type, 0)
366
+ if _no_transform_needed(inner_type):
367
+ # for some types there is no need to transform anything, so we can get a small
368
+ # perf boost from skipping that work.
369
+ #
370
+ # but we still need to convert to a list to ensure the data is json-serializable
371
+ if is_list(data):
372
+ return data
373
+ return list(data)
374
+
375
+ return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]
376
+
377
+ if is_union_type(stripped_type):
378
+ # For union types we run the transformation against all subtypes to ensure that everything is transformed.
379
+ #
380
+ # TODO: there may be edge cases where the same normalized field name will transform to two different names
381
+ # in different subtypes.
382
+ for subtype in get_args(stripped_type):
383
+ data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype)
384
+ return data
385
+
386
+ if isinstance(data, pydantic.BaseModel):
387
+ return model_dump(data, exclude_unset=True, mode="json")
388
+
389
+ annotated_type = _get_annotated_type(annotation)
390
+ if annotated_type is None:
391
+ return data
392
+
393
+ # ignore the first argument as it is the actual type
394
+ annotations = get_args(annotated_type)[1:]
395
+ for annotation in annotations:
396
+ if isinstance(annotation, PropertyInfo) and annotation.format is not None:
397
+ return await _async_format_data(data, annotation.format, annotation.format_template)
398
+
399
+ return data
400
+
401
+
402
+ async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object:
403
+ if isinstance(data, (date, datetime)):
404
+ if format_ == "iso8601":
405
+ return data.isoformat()
406
+
407
+ if format_ == "custom" and format_template is not None:
408
+ return data.strftime(format_template)
409
+
410
+ if format_ == "base64" and is_base64_file_input(data):
411
+ binary: str | bytes | None = None
412
+
413
+ if isinstance(data, pathlib.Path):
414
+ binary = await anyio.Path(data).read_bytes()
415
+ elif isinstance(data, io.IOBase):
416
+ binary = data.read()
417
+
418
+ if isinstance(binary, str): # type: ignore[unreachable]
419
+ binary = binary.encode()
420
+
421
+ if not isinstance(binary, bytes):
422
+ raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}")
423
+
424
+ return base64.b64encode(binary).decode("ascii")
425
+
426
+ return data
427
+
428
+
429
+ async def _async_transform_typeddict(
430
+ data: Mapping[str, object],
431
+ expected_type: type,
432
+ ) -> Mapping[str, object]:
433
+ result: dict[str, object] = {}
434
+ annotations = get_type_hints(expected_type, include_extras=True)
435
+ for key, value in data.items():
436
+ if not is_given(value):
437
+ # we don't need to include omitted values here as they'll
438
+ # be stripped out before the request is sent anyway
439
+ continue
440
+
441
+ type_ = annotations.get(key)
442
+ if type_ is None:
443
+ # we do not have a type annotation for this field, leave it as is
444
+ result[key] = value
445
+ else:
446
+ result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_)
447
+ return result
448
+
449
+
450
+ @lru_cache(maxsize=8096)
451
+ def get_type_hints(
452
+ obj: Any,
453
+ globalns: dict[str, Any] | None = None,
454
+ localns: Mapping[str, Any] | None = None,
455
+ include_extras: bool = False,
456
+ ) -> dict[str, Any]:
457
+ return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras)
.venv311/Lib/site-packages/openai/_utils/_typing.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ import typing
5
+ import typing_extensions
6
+ from typing import Any, TypeVar, Iterable, cast
7
+ from collections import abc as _c_abc
8
+ from typing_extensions import (
9
+ TypeIs,
10
+ Required,
11
+ Annotated,
12
+ get_args,
13
+ get_origin,
14
+ )
15
+
16
+ from ._utils import lru_cache
17
+ from .._types import InheritsGeneric
18
+ from ._compat import is_union as _is_union
19
+
20
+
21
+ def is_annotated_type(typ: type) -> bool:
22
+ return get_origin(typ) == Annotated
23
+
24
+
25
+ def is_list_type(typ: type) -> bool:
26
+ return (get_origin(typ) or typ) == list
27
+
28
+
29
+ def is_sequence_type(typ: type) -> bool:
30
+ origin = get_origin(typ) or typ
31
+ return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence
32
+
33
+
34
+ def is_iterable_type(typ: type) -> bool:
35
+ """If the given type is `typing.Iterable[T]`"""
36
+ origin = get_origin(typ) or typ
37
+ return origin == Iterable or origin == _c_abc.Iterable
38
+
39
+
40
+ def is_union_type(typ: type) -> bool:
41
+ return _is_union(get_origin(typ))
42
+
43
+
44
+ def is_required_type(typ: type) -> bool:
45
+ return get_origin(typ) == Required
46
+
47
+
48
+ def is_typevar(typ: type) -> bool:
49
+ # type ignore is required because type checkers
50
+ # think this expression will always return False
51
+ return type(typ) == TypeVar # type: ignore
52
+
53
+
54
+ _TYPE_ALIAS_TYPES: tuple[type[typing_extensions.TypeAliasType], ...] = (typing_extensions.TypeAliasType,)
55
+ if sys.version_info >= (3, 12):
56
+ _TYPE_ALIAS_TYPES = (*_TYPE_ALIAS_TYPES, typing.TypeAliasType)
57
+
58
+
59
+ def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]:
60
+ """Return whether the provided argument is an instance of `TypeAliasType`.
61
+
62
+ ```python
63
+ type Int = int
64
+ is_type_alias_type(Int)
65
+ # > True
66
+ Str = TypeAliasType("Str", str)
67
+ is_type_alias_type(Str)
68
+ # > True
69
+ ```
70
+ """
71
+ return isinstance(tp, _TYPE_ALIAS_TYPES)
72
+
73
+
74
+ # Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]]
75
+ @lru_cache(maxsize=8096)
76
+ def strip_annotated_type(typ: type) -> type:
77
+ if is_required_type(typ) or is_annotated_type(typ):
78
+ return strip_annotated_type(cast(type, get_args(typ)[0]))
79
+
80
+ return typ
81
+
82
+
83
+ def extract_type_arg(typ: type, index: int) -> type:
84
+ args = get_args(typ)
85
+ try:
86
+ return cast(type, args[index])
87
+ except IndexError as err:
88
+ raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") from err
89
+
90
+
91
+ def extract_type_var_from_base(
92
+ typ: type,
93
+ *,
94
+ generic_bases: tuple[type, ...],
95
+ index: int,
96
+ failure_message: str | None = None,
97
+ ) -> type:
98
+ """Given a type like `Foo[T]`, returns the generic type variable `T`.
99
+
100
+ This also handles the case where a concrete subclass is given, e.g.
101
+ ```py
102
+ class MyResponse(Foo[bytes]):
103
+ ...
104
+
105
+ extract_type_var(MyResponse, bases=(Foo,), index=0) -> bytes
106
+ ```
107
+
108
+ And where a generic subclass is given:
109
+ ```py
110
+ _T = TypeVar('_T')
111
+ class MyResponse(Foo[_T]):
112
+ ...
113
+
114
+ extract_type_var(MyResponse[bytes], bases=(Foo,), index=0) -> bytes
115
+ ```
116
+ """
117
+ cls = cast(object, get_origin(typ) or typ)
118
+ if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains]
119
+ # we're given the class directly
120
+ return extract_type_arg(typ, index)
121
+
122
+ # if a subclass is given
123
+ # ---
124
+ # this is needed as __orig_bases__ is not present in the typeshed stubs
125
+ # because it is intended to be for internal use only, however there does
126
+ # not seem to be a way to resolve generic TypeVars for inherited subclasses
127
+ # without using it.
128
+ if isinstance(cls, InheritsGeneric):
129
+ target_base_class: Any | None = None
130
+ for base in cls.__orig_bases__:
131
+ if base.__origin__ in generic_bases:
132
+ target_base_class = base
133
+ break
134
+
135
+ if target_base_class is None:
136
+ raise RuntimeError(
137
+ "Could not find the generic base class;\n"
138
+ "This should never happen;\n"
139
+ f"Does {cls} inherit from one of {generic_bases} ?"
140
+ )
141
+
142
+ extracted = extract_type_arg(target_base_class, index)
143
+ if is_typevar(extracted):
144
+ # If the extracted type argument is itself a type variable
145
+ # then that means the subclass itself is generic, so we have
146
+ # to resolve the type argument from the class itself, not
147
+ # the base class.
148
+ #
149
+ # Note: if there is more than 1 type argument, the subclass could
150
+ # change the ordering of the type arguments, this is not currently
151
+ # supported.
152
+ return extract_type_arg(typ, index)
153
+
154
+ return extracted
155
+
156
+ raise RuntimeError(failure_message or f"Could not resolve inner type variable at index {index} for {typ}")
.venv311/Lib/site-packages/openai/_utils/_utils.py ADDED
@@ -0,0 +1,449 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import re
5
+ import inspect
6
+ import functools
7
+ from typing import (
8
+ TYPE_CHECKING,
9
+ Any,
10
+ Tuple,
11
+ Mapping,
12
+ TypeVar,
13
+ Callable,
14
+ Iterable,
15
+ Sequence,
16
+ cast,
17
+ overload,
18
+ )
19
+ from pathlib import Path
20
+ from datetime import date, datetime
21
+ from typing_extensions import TypeGuard, get_args
22
+
23
+ import sniffio
24
+
25
+ from .._types import Omit, NotGiven, FileTypes, ArrayFormat, HeadersLike
26
+
27
+ _T = TypeVar("_T")
28
+ _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...])
29
+ _MappingT = TypeVar("_MappingT", bound=Mapping[str, object])
30
+ _SequenceT = TypeVar("_SequenceT", bound=Sequence[object])
31
+ CallableT = TypeVar("CallableT", bound=Callable[..., Any])
32
+
33
+ if TYPE_CHECKING:
34
+ from ..lib.azure import AzureOpenAI, AsyncAzureOpenAI
35
+
36
+
37
+ def flatten(t: Iterable[Iterable[_T]]) -> list[_T]:
38
+ return [item for sublist in t for item in sublist]
39
+
40
+
41
+ def extract_files(
42
+ # TODO: this needs to take Dict but variance issues.....
43
+ # create protocol type ?
44
+ query: Mapping[str, object],
45
+ *,
46
+ paths: Sequence[Sequence[str]],
47
+ array_format: ArrayFormat = "brackets",
48
+ ) -> list[tuple[str, FileTypes]]:
49
+ """Recursively extract files from the given dictionary based on specified paths.
50
+
51
+ A path may look like this ['foo', 'files', '<array>', 'data'].
52
+
53
+ ``array_format`` controls how ``<array>`` segments contribute to the emitted
54
+ field name. Supported values: ``"brackets"`` (``foo[]``), ``"repeat"`` and
55
+ ``"comma"`` (``foo``), ``"indices"`` (``foo[0]``, ``foo[1]``).
56
+
57
+ Note: this mutates the given dictionary.
58
+ """
59
+ files: list[tuple[str, FileTypes]] = []
60
+ for path in paths:
61
+ files.extend(_extract_items(query, path, index=0, flattened_key=None, array_format=array_format))
62
+ return files
63
+
64
+
65
+ def _array_suffix(array_format: ArrayFormat, array_index: int) -> str:
66
+ if array_format == "brackets":
67
+ return "[]"
68
+ if array_format == "indices":
69
+ return f"[{array_index}]"
70
+ if array_format == "repeat" or array_format == "comma":
71
+ # Both repeat the bare field name for each file part; there is no
72
+ # meaningful way to comma-join binary parts.
73
+ return ""
74
+ raise NotImplementedError(
75
+ f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}"
76
+ )
77
+
78
+
79
+ def _extract_items(
80
+ obj: object,
81
+ path: Sequence[str],
82
+ *,
83
+ index: int,
84
+ flattened_key: str | None,
85
+ array_format: ArrayFormat,
86
+ ) -> list[tuple[str, FileTypes]]:
87
+ try:
88
+ key = path[index]
89
+ except IndexError:
90
+ if not is_given(obj):
91
+ # no value was provided - we can safely ignore
92
+ return []
93
+
94
+ # cyclical import
95
+ from .._files import assert_is_file_content
96
+
97
+ # We have exhausted the path, return the entry we found.
98
+ assert flattened_key is not None
99
+
100
+ if is_list(obj):
101
+ files: list[tuple[str, FileTypes]] = []
102
+ for array_index, entry in enumerate(obj):
103
+ suffix = _array_suffix(array_format, array_index)
104
+ emitted_key = (flattened_key + suffix) if flattened_key else suffix
105
+ assert_is_file_content(entry, key=emitted_key)
106
+ files.append((emitted_key, cast(FileTypes, entry)))
107
+ return files
108
+
109
+ assert_is_file_content(obj, key=flattened_key)
110
+ return [(flattened_key, cast(FileTypes, obj))]
111
+
112
+ index += 1
113
+ if is_dict(obj):
114
+ try:
115
+ # Remove the field if there are no more dict keys in the path,
116
+ # only "<array>" traversal markers or end.
117
+ if all(p == "<array>" for p in path[index:]):
118
+ item = obj.pop(key)
119
+ else:
120
+ item = obj[key]
121
+ except KeyError:
122
+ # Key was not present in the dictionary, this is not indicative of an error
123
+ # as the given path may not point to a required field. We also do not want
124
+ # to enforce required fields as the API may differ from the spec in some cases.
125
+ return []
126
+ if flattened_key is None:
127
+ flattened_key = key
128
+ else:
129
+ flattened_key += f"[{key}]"
130
+ return _extract_items(
131
+ item,
132
+ path,
133
+ index=index,
134
+ flattened_key=flattened_key,
135
+ array_format=array_format,
136
+ )
137
+ elif is_list(obj):
138
+ if key != "<array>":
139
+ return []
140
+
141
+ return flatten(
142
+ [
143
+ _extract_items(
144
+ item,
145
+ path,
146
+ index=index,
147
+ flattened_key=(
148
+ (flattened_key if flattened_key is not None else "") + _array_suffix(array_format, array_index)
149
+ ),
150
+ array_format=array_format,
151
+ )
152
+ for array_index, item in enumerate(obj)
153
+ ]
154
+ )
155
+
156
+ # Something unexpected was passed, just ignore it.
157
+ return []
158
+
159
+
160
+ def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]:
161
+ return not isinstance(obj, NotGiven) and not isinstance(obj, Omit)
162
+
163
+
164
+ # Type safe methods for narrowing types with TypeVars.
165
+ # The default narrowing for isinstance(obj, dict) is dict[unknown, unknown],
166
+ # however this cause Pyright to rightfully report errors. As we know we don't
167
+ # care about the contained types we can safely use `object` in its place.
168
+ #
169
+ # There are two separate functions defined, `is_*` and `is_*_t` for different use cases.
170
+ # `is_*` is for when you're dealing with an unknown input
171
+ # `is_*_t` is for when you're narrowing a known union type to a specific subset
172
+
173
+
174
+ def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]:
175
+ return isinstance(obj, tuple)
176
+
177
+
178
+ def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]:
179
+ return isinstance(obj, tuple)
180
+
181
+
182
+ def is_sequence(obj: object) -> TypeGuard[Sequence[object]]:
183
+ return isinstance(obj, Sequence)
184
+
185
+
186
+ def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]:
187
+ return isinstance(obj, Sequence)
188
+
189
+
190
+ def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]:
191
+ return isinstance(obj, Mapping)
192
+
193
+
194
+ def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]:
195
+ return isinstance(obj, Mapping)
196
+
197
+
198
+ def is_dict(obj: object) -> TypeGuard[dict[object, object]]:
199
+ return isinstance(obj, dict)
200
+
201
+
202
+ def is_list(obj: object) -> TypeGuard[list[object]]:
203
+ return isinstance(obj, list)
204
+
205
+
206
+ def is_iterable(obj: object) -> TypeGuard[Iterable[object]]:
207
+ return isinstance(obj, Iterable)
208
+
209
+
210
+ # copied from https://github.com/Rapptz/RoboDanny
211
+ def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str:
212
+ size = len(seq)
213
+ if size == 0:
214
+ return ""
215
+
216
+ if size == 1:
217
+ return seq[0]
218
+
219
+ if size == 2:
220
+ return f"{seq[0]} {final} {seq[1]}"
221
+
222
+ return delim.join(seq[:-1]) + f" {final} {seq[-1]}"
223
+
224
+
225
+ def quote(string: str) -> str:
226
+ """Add single quotation marks around the given string. Does *not* do any escaping."""
227
+ return f"'{string}'"
228
+
229
+
230
+ def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]:
231
+ """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function.
232
+
233
+ Useful for enforcing runtime validation of overloaded functions.
234
+
235
+ Example usage:
236
+ ```py
237
+ @overload
238
+ def foo(*, a: str) -> str: ...
239
+
240
+
241
+ @overload
242
+ def foo(*, b: bool) -> str: ...
243
+
244
+
245
+ # This enforces the same constraints that a static type checker would
246
+ # i.e. that either a or b must be passed to the function
247
+ @required_args(["a"], ["b"])
248
+ def foo(*, a: str | None = None, b: bool | None = None) -> str: ...
249
+ ```
250
+ """
251
+
252
+ def inner(func: CallableT) -> CallableT:
253
+ params = inspect.signature(func).parameters
254
+ positional = [
255
+ name
256
+ for name, param in params.items()
257
+ if param.kind
258
+ in {
259
+ param.POSITIONAL_ONLY,
260
+ param.POSITIONAL_OR_KEYWORD,
261
+ }
262
+ ]
263
+
264
+ @functools.wraps(func)
265
+ def wrapper(*args: object, **kwargs: object) -> object:
266
+ given_params: set[str] = set()
267
+ for i, _ in enumerate(args):
268
+ try:
269
+ given_params.add(positional[i])
270
+ except IndexError:
271
+ raise TypeError(
272
+ f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given"
273
+ ) from None
274
+
275
+ for key in kwargs.keys():
276
+ given_params.add(key)
277
+
278
+ for variant in variants:
279
+ matches = all((param in given_params for param in variant))
280
+ if matches:
281
+ break
282
+ else: # no break
283
+ if len(variants) > 1:
284
+ variations = human_join(
285
+ ["(" + human_join([quote(arg) for arg in variant], final="and") + ")" for variant in variants]
286
+ )
287
+ msg = f"Missing required arguments; Expected either {variations} arguments to be given"
288
+ else:
289
+ assert len(variants) > 0
290
+
291
+ # TODO: this error message is not deterministic
292
+ missing = list(set(variants[0]) - given_params)
293
+ if len(missing) > 1:
294
+ msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}"
295
+ else:
296
+ msg = f"Missing required argument: {quote(missing[0])}"
297
+ raise TypeError(msg)
298
+ return func(*args, **kwargs)
299
+
300
+ return wrapper # type: ignore
301
+
302
+ return inner
303
+
304
+
305
+ _K = TypeVar("_K")
306
+ _V = TypeVar("_V")
307
+
308
+
309
+ @overload
310
+ def strip_not_given(obj: None) -> None: ...
311
+
312
+
313
+ @overload
314
+ def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ...
315
+
316
+
317
+ @overload
318
+ def strip_not_given(obj: object) -> object: ...
319
+
320
+
321
+ def strip_not_given(obj: object | None) -> object:
322
+ """Remove all top-level keys where their values are instances of `NotGiven`"""
323
+ if obj is None:
324
+ return None
325
+
326
+ if not is_mapping(obj):
327
+ return obj
328
+
329
+ return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)}
330
+
331
+
332
+ def coerce_integer(val: str) -> int:
333
+ return int(val, base=10)
334
+
335
+
336
+ def coerce_float(val: str) -> float:
337
+ return float(val)
338
+
339
+
340
+ def coerce_boolean(val: str) -> bool:
341
+ return val == "true" or val == "1" or val == "on"
342
+
343
+
344
+ def maybe_coerce_integer(val: str | None) -> int | None:
345
+ if val is None:
346
+ return None
347
+ return coerce_integer(val)
348
+
349
+
350
+ def maybe_coerce_float(val: str | None) -> float | None:
351
+ if val is None:
352
+ return None
353
+ return coerce_float(val)
354
+
355
+
356
+ def maybe_coerce_boolean(val: str | None) -> bool | None:
357
+ if val is None:
358
+ return None
359
+ return coerce_boolean(val)
360
+
361
+
362
+ def removeprefix(string: str, prefix: str) -> str:
363
+ """Remove a prefix from a string.
364
+
365
+ Backport of `str.removeprefix` for Python < 3.9
366
+ """
367
+ if string.startswith(prefix):
368
+ return string[len(prefix) :]
369
+ return string
370
+
371
+
372
+ def removesuffix(string: str, suffix: str) -> str:
373
+ """Remove a suffix from a string.
374
+
375
+ Backport of `str.removesuffix` for Python < 3.9
376
+ """
377
+ if string.endswith(suffix):
378
+ return string[: -len(suffix)]
379
+ return string
380
+
381
+
382
+ def file_from_path(path: str) -> FileTypes:
383
+ contents = Path(path).read_bytes()
384
+ file_name = os.path.basename(path)
385
+ return (file_name, contents)
386
+
387
+
388
+ def get_required_header(headers: HeadersLike, header: str) -> str:
389
+ lower_header = header.lower()
390
+ if is_mapping_t(headers):
391
+ # mypy doesn't understand the type narrowing here
392
+ for k, v in headers.items(): # type: ignore
393
+ if k.lower() == lower_header and isinstance(v, str):
394
+ return v
395
+
396
+ # to deal with the case where the header looks like Stainless-Event-Id
397
+ intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize())
398
+
399
+ for normalized_header in [header, lower_header, header.upper(), intercaps_header]:
400
+ value = headers.get(normalized_header)
401
+ if value:
402
+ return value
403
+
404
+ raise ValueError(f"Could not find {header} header")
405
+
406
+
407
+ def get_async_library() -> str:
408
+ try:
409
+ return sniffio.current_async_library()
410
+ except Exception:
411
+ return "false"
412
+
413
+
414
+ def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]:
415
+ """A version of functools.lru_cache that retains the type signature
416
+ for the wrapped function arguments.
417
+ """
418
+ wrapper = functools.lru_cache( # noqa: TID251
419
+ maxsize=maxsize,
420
+ )
421
+ return cast(Any, wrapper) # type: ignore[no-any-return]
422
+
423
+
424
+ def json_safe(data: object) -> object:
425
+ """Translates a mapping / sequence recursively in the same fashion
426
+ as `pydantic` v2's `model_dump(mode="json")`.
427
+ """
428
+ if is_mapping(data):
429
+ return {json_safe(key): json_safe(value) for key, value in data.items()}
430
+
431
+ if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)):
432
+ return [json_safe(item) for item in data]
433
+
434
+ if isinstance(data, (datetime, date)):
435
+ return data.isoformat()
436
+
437
+ return data
438
+
439
+
440
+ def is_azure_client(client: object) -> TypeGuard[AzureOpenAI]:
441
+ from ..lib.azure import AzureOpenAI
442
+
443
+ return isinstance(client, AzureOpenAI)
444
+
445
+
446
+ def is_async_azure_client(client: object) -> TypeGuard[AsyncAzureOpenAI]:
447
+ from ..lib.azure import AsyncAzureOpenAI
448
+
449
+ return isinstance(client, AsyncAzureOpenAI)
.venv311/Lib/site-packages/openai/types/admin/organization/projects/__init__.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from .project_user import ProjectUser as ProjectUser
6
+ from .project_group import ProjectGroup as ProjectGroup
7
+ from .project_api_key import ProjectAPIKey as ProjectAPIKey
8
+ from .role_list_params import RoleListParams as RoleListParams
9
+ from .user_list_params import UserListParams as UserListParams
10
+ from .group_list_params import GroupListParams as GroupListParams
11
+ from .project_rate_limit import ProjectRateLimit as ProjectRateLimit
12
+ from .role_create_params import RoleCreateParams as RoleCreateParams
13
+ from .role_update_params import RoleUpdateParams as RoleUpdateParams
14
+ from .user_create_params import UserCreateParams as UserCreateParams
15
+ from .user_update_params import UserUpdateParams as UserUpdateParams
16
+ from .api_key_list_params import APIKeyListParams as APIKeyListParams
17
+ from .group_create_params import GroupCreateParams as GroupCreateParams
18
+ from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse
19
+ from .user_delete_response import UserDeleteResponse as UserDeleteResponse
20
+ from .group_delete_response import GroupDeleteResponse as GroupDeleteResponse
21
+ from .api_key_delete_response import APIKeyDeleteResponse as APIKeyDeleteResponse
22
+ from .certificate_list_params import CertificateListParams as CertificateListParams
23
+ from .project_service_account import ProjectServiceAccount as ProjectServiceAccount
24
+ from .certificate_list_response import CertificateListResponse as CertificateListResponse
25
+ from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams
26
+ from .service_account_list_params import ServiceAccountListParams as ServiceAccountListParams
27
+ from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse
28
+ from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams
29
+ from .service_account_create_params import ServiceAccountCreateParams as ServiceAccountCreateParams
30
+ from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse
31
+ from .service_account_create_response import ServiceAccountCreateResponse as ServiceAccountCreateResponse
32
+ from .service_account_delete_response import ServiceAccountDeleteResponse as ServiceAccountDeleteResponse
33
+ from .rate_limit_list_rate_limits_params import RateLimitListRateLimitsParams as RateLimitListRateLimitsParams
34
+ from .rate_limit_update_rate_limit_params import RateLimitUpdateRateLimitParams as RateLimitUpdateRateLimitParams
.venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/__init__.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from .role_list_params import RoleListParams as RoleListParams
6
+ from .role_create_params import RoleCreateParams as RoleCreateParams
7
+ from .role_list_response import RoleListResponse as RoleListResponse
8
+ from .role_create_response import RoleCreateResponse as RoleCreateResponse
9
+ from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse
.venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_create_params.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing_extensions import Required, TypedDict
6
+
7
+ __all__ = ["RoleCreateParams"]
8
+
9
+
10
+ class RoleCreateParams(TypedDict, total=False):
11
+ project_id: Required[str]
12
+
13
+ role_id: Required[str]
14
+ """Identifier of the role to assign."""
.venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_create_response.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from typing_extensions import Literal
4
+
5
+ from ...role import Role
6
+ from ......_models import BaseModel
7
+
8
+ __all__ = ["RoleCreateResponse", "Group"]
9
+
10
+
11
+ class Group(BaseModel):
12
+ """Summary information about a group returned in role assignment responses."""
13
+
14
+ id: str
15
+ """Identifier for the group."""
16
+
17
+ created_at: int
18
+ """Unix timestamp (in seconds) when the group was created."""
19
+
20
+ name: str
21
+ """Display name of the group."""
22
+
23
+ object: Literal["group"]
24
+ """Always `group`."""
25
+
26
+ scim_managed: bool
27
+ """Whether the group is managed through SCIM."""
28
+
29
+
30
+ class RoleCreateResponse(BaseModel):
31
+ """Role assignment linking a group to a role."""
32
+
33
+ group: Group
34
+ """Summary information about a group returned in role assignment responses."""
35
+
36
+ object: Literal["group.role"]
37
+ """Always `group.role`."""
38
+
39
+ role: Role
40
+ """Details about a role that can be assigned through the public Roles API."""
.venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_delete_response.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from ......_models import BaseModel
4
+
5
+ __all__ = ["RoleDeleteResponse"]
6
+
7
+
8
+ class RoleDeleteResponse(BaseModel):
9
+ """Confirmation payload returned after unassigning a role."""
10
+
11
+ deleted: bool
12
+ """Whether the assignment was removed."""
13
+
14
+ object: str
15
+ """
16
+ Identifier for the deleted assignment, such as `group.role.deleted` or
17
+ `user.role.deleted`.
18
+ """
.venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_list_params.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing_extensions import Literal, Required, TypedDict
6
+
7
+ __all__ = ["RoleListParams"]
8
+
9
+
10
+ class RoleListParams(TypedDict, total=False):
11
+ project_id: Required[str]
12
+
13
+ after: str
14
+ """Cursor for pagination.
15
+
16
+ Provide the value from the previous response's `next` field to continue listing
17
+ project roles.
18
+ """
19
+
20
+ limit: int
21
+ """A limit on the number of project role assignments to return."""
22
+
23
+ order: Literal["asc", "desc"]
24
+ """Sort order for the returned project roles."""
.venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_list_response.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from typing import Dict, List, Optional
4
+
5
+ from ......_models import BaseModel
6
+
7
+ __all__ = ["RoleListResponse"]
8
+
9
+
10
+ class RoleListResponse(BaseModel):
11
+ """
12
+ Detailed information about a role assignment entry returned when listing assignments.
13
+ """
14
+
15
+ id: str
16
+ """Identifier for the role."""
17
+
18
+ created_at: Optional[int] = None
19
+ """When the role was created."""
20
+
21
+ created_by: Optional[str] = None
22
+ """Identifier of the actor who created the role."""
23
+
24
+ created_by_user_obj: Optional[Dict[str, object]] = None
25
+ """User details for the actor that created the role, when available."""
26
+
27
+ description: Optional[str] = None
28
+ """Description of the role."""
29
+
30
+ metadata: Optional[Dict[str, object]] = None
31
+ """Arbitrary metadata stored on the role."""
32
+
33
+ name: str
34
+ """Name of the role."""
35
+
36
+ permissions: List[str]
37
+ """Permissions associated with the role."""
38
+
39
+ predefined_role: bool
40
+ """Whether the role is predefined by OpenAI."""
41
+
42
+ resource_type: str
43
+ """Resource type the role applies to."""
44
+
45
+ updated_at: Optional[int] = None
46
+ """When the role was last updated."""
.venv311/Lib/site-packages/openai/types/admin/organization/projects/user_delete_response.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from typing_extensions import Literal
4
+
5
+ from ....._models import BaseModel
6
+
7
+ __all__ = ["UserDeleteResponse"]
8
+
9
+
10
+ class UserDeleteResponse(BaseModel):
11
+ id: str
12
+
13
+ deleted: bool
14
+
15
+ object: Literal["organization.project.user.deleted"]
.venv311/Lib/site-packages/openai/types/admin/organization/projects/user_update_params.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+ from typing_extensions import Required, TypedDict
7
+
8
+ __all__ = ["UserUpdateParams"]
9
+
10
+
11
+ class UserUpdateParams(TypedDict, total=False):
12
+ project_id: Required[str]
13
+
14
+ role: Optional[str]
15
+ """`owner` or `member`"""
.venv311/Lib/site-packages/openai/types/admin/organization/projects/users/__init__.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from .role_list_params import RoleListParams as RoleListParams
6
+ from .role_create_params import RoleCreateParams as RoleCreateParams
7
+ from .role_list_response import RoleListResponse as RoleListResponse
8
+ from .role_create_response import RoleCreateResponse as RoleCreateResponse
9
+ from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse
.venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_create_params.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing_extensions import Required, TypedDict
6
+
7
+ __all__ = ["RoleCreateParams"]
8
+
9
+
10
+ class RoleCreateParams(TypedDict, total=False):
11
+ project_id: Required[str]
12
+
13
+ role_id: Required[str]
14
+ """Identifier of the role to assign."""
.venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_create_response.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from typing_extensions import Literal
4
+
5
+ from ...role import Role
6
+ from ......_models import BaseModel
7
+ from ...organization_user import OrganizationUser
8
+
9
+ __all__ = ["RoleCreateResponse"]
10
+
11
+
12
+ class RoleCreateResponse(BaseModel):
13
+ """Role assignment linking a user to a role."""
14
+
15
+ object: Literal["user.role"]
16
+ """Always `user.role`."""
17
+
18
+ role: Role
19
+ """Details about a role that can be assigned through the public Roles API."""
20
+
21
+ user: OrganizationUser
22
+ """Represents an individual `user` within an organization."""
.venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_delete_response.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from ......_models import BaseModel
4
+
5
+ __all__ = ["RoleDeleteResponse"]
6
+
7
+
8
+ class RoleDeleteResponse(BaseModel):
9
+ """Confirmation payload returned after unassigning a role."""
10
+
11
+ deleted: bool
12
+ """Whether the assignment was removed."""
13
+
14
+ object: str
15
+ """
16
+ Identifier for the deleted assignment, such as `group.role.deleted` or
17
+ `user.role.deleted`.
18
+ """
.venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_list_params.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing_extensions import Literal, Required, TypedDict
6
+
7
+ __all__ = ["RoleListParams"]
8
+
9
+
10
+ class RoleListParams(TypedDict, total=False):
11
+ project_id: Required[str]
12
+
13
+ after: str
14
+ """Cursor for pagination.
15
+
16
+ Provide the value from the previous response's `next` field to continue listing
17
+ project roles.
18
+ """
19
+
20
+ limit: int
21
+ """A limit on the number of project role assignments to return."""
22
+
23
+ order: Literal["asc", "desc"]
24
+ """Sort order for the returned project roles."""
.venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_list_response.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from typing import Dict, List, Optional
4
+
5
+ from ......_models import BaseModel
6
+
7
+ __all__ = ["RoleListResponse"]
8
+
9
+
10
+ class RoleListResponse(BaseModel):
11
+ """
12
+ Detailed information about a role assignment entry returned when listing assignments.
13
+ """
14
+
15
+ id: str
16
+ """Identifier for the role."""
17
+
18
+ created_at: Optional[int] = None
19
+ """When the role was created."""
20
+
21
+ created_by: Optional[str] = None
22
+ """Identifier of the actor who created the role."""
23
+
24
+ created_by_user_obj: Optional[Dict[str, object]] = None
25
+ """User details for the actor that created the role, when available."""
26
+
27
+ description: Optional[str] = None
28
+ """Description of the role."""
29
+
30
+ metadata: Optional[Dict[str, object]] = None
31
+ """Arbitrary metadata stored on the role."""
32
+
33
+ name: str
34
+ """Name of the role."""
35
+
36
+ permissions: List[str]
37
+ """Permissions associated with the role."""
38
+
39
+ predefined_role: bool
40
+ """Whether the role is predefined by OpenAI."""
41
+
42
+ resource_type: str
43
+ """Resource type the role applies to."""
44
+
45
+ updated_at: Optional[int] = None
46
+ """When the role was last updated."""
.venv311/Lib/site-packages/openai/types/admin/organization/users/__init__.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from .role_list_params import RoleListParams as RoleListParams
6
+ from .role_create_params import RoleCreateParams as RoleCreateParams
7
+ from .role_list_response import RoleListResponse as RoleListResponse
8
+ from .role_create_response import RoleCreateResponse as RoleCreateResponse
9
+ from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse
.venv311/Lib/site-packages/openai/types/admin/organization/users/role_create_params.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing_extensions import Required, TypedDict
6
+
7
+ __all__ = ["RoleCreateParams"]
8
+
9
+
10
+ class RoleCreateParams(TypedDict, total=False):
11
+ role_id: Required[str]
12
+ """Identifier of the role to assign."""
.venv311/Lib/site-packages/openai/types/admin/organization/users/role_create_response.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from typing_extensions import Literal
4
+
5
+ from ..role import Role
6
+ from ....._models import BaseModel
7
+ from ..organization_user import OrganizationUser
8
+
9
+ __all__ = ["RoleCreateResponse"]
10
+
11
+
12
+ class RoleCreateResponse(BaseModel):
13
+ """Role assignment linking a user to a role."""
14
+
15
+ object: Literal["user.role"]
16
+ """Always `user.role`."""
17
+
18
+ role: Role
19
+ """Details about a role that can be assigned through the public Roles API."""
20
+
21
+ user: OrganizationUser
22
+ """Represents an individual `user` within an organization."""
.venv311/Lib/site-packages/openai/types/admin/organization/users/role_delete_response.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from ....._models import BaseModel
4
+
5
+ __all__ = ["RoleDeleteResponse"]
6
+
7
+
8
+ class RoleDeleteResponse(BaseModel):
9
+ """Confirmation payload returned after unassigning a role."""
10
+
11
+ deleted: bool
12
+ """Whether the assignment was removed."""
13
+
14
+ object: str
15
+ """
16
+ Identifier for the deleted assignment, such as `group.role.deleted` or
17
+ `user.role.deleted`.
18
+ """
.venv311/Lib/site-packages/openai/types/admin/organization/users/role_list_params.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing_extensions import Literal, TypedDict
6
+
7
+ __all__ = ["RoleListParams"]
8
+
9
+
10
+ class RoleListParams(TypedDict, total=False):
11
+ after: str
12
+ """Cursor for pagination.
13
+
14
+ Provide the value from the previous response's `next` field to continue listing
15
+ organization roles.
16
+ """
17
+
18
+ limit: int
19
+ """A limit on the number of organization role assignments to return."""
20
+
21
+ order: Literal["asc", "desc"]
22
+ """Sort order for the returned organization roles."""
.venv311/Lib/site-packages/openai/types/admin/organization/users/role_list_response.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from typing import Dict, List, Optional
4
+
5
+ from ....._models import BaseModel
6
+
7
+ __all__ = ["RoleListResponse"]
8
+
9
+
10
+ class RoleListResponse(BaseModel):
11
+ """
12
+ Detailed information about a role assignment entry returned when listing assignments.
13
+ """
14
+
15
+ id: str
16
+ """Identifier for the role."""
17
+
18
+ created_at: Optional[int] = None
19
+ """When the role was created."""
20
+
21
+ created_by: Optional[str] = None
22
+ """Identifier of the actor who created the role."""
23
+
24
+ created_by_user_obj: Optional[Dict[str, object]] = None
25
+ """User details for the actor that created the role, when available."""
26
+
27
+ description: Optional[str] = None
28
+ """Description of the role."""
29
+
30
+ metadata: Optional[Dict[str, object]] = None
31
+ """Arbitrary metadata stored on the role."""
32
+
33
+ name: str
34
+ """Name of the role."""
35
+
36
+ permissions: List[str]
37
+ """Permissions associated with the role."""
38
+
39
+ predefined_role: bool
40
+ """Whether the role is predefined by OpenAI."""
41
+
42
+ resource_type: str
43
+ """Resource type the role applies to."""
44
+
45
+ updated_at: Optional[int] = None
46
+ """When the role was last updated."""
.venv311/Lib/site-packages/openai/types/audio/__init__.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from .translation import Translation as Translation
6
+ from .speech_model import SpeechModel as SpeechModel
7
+ from .transcription import Transcription as Transcription
8
+ from .transcription_word import TranscriptionWord as TranscriptionWord
9
+ from .translation_verbose import TranslationVerbose as TranslationVerbose
10
+ from .speech_create_params import SpeechCreateParams as SpeechCreateParams
11
+ from .transcription_include import TranscriptionInclude as TranscriptionInclude
12
+ from .transcription_segment import TranscriptionSegment as TranscriptionSegment
13
+ from .transcription_verbose import TranscriptionVerbose as TranscriptionVerbose
14
+ from .transcription_diarized import TranscriptionDiarized as TranscriptionDiarized
15
+ from .translation_create_params import TranslationCreateParams as TranslationCreateParams
16
+ from .transcription_stream_event import TranscriptionStreamEvent as TranscriptionStreamEvent
17
+ from .transcription_create_params import TranscriptionCreateParams as TranscriptionCreateParams
18
+ from .translation_create_response import TranslationCreateResponse as TranslationCreateResponse
19
+ from .transcription_create_response import TranscriptionCreateResponse as TranscriptionCreateResponse
20
+ from .transcription_text_done_event import TranscriptionTextDoneEvent as TranscriptionTextDoneEvent
21
+ from .transcription_diarized_segment import TranscriptionDiarizedSegment as TranscriptionDiarizedSegment
22
+ from .transcription_text_delta_event import TranscriptionTextDeltaEvent as TranscriptionTextDeltaEvent
23
+ from .transcription_text_segment_event import TranscriptionTextSegmentEvent as TranscriptionTextSegmentEvent
.venv311/Lib/site-packages/openai/types/audio/speech_create_params.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Union
6
+ from typing_extensions import Literal, Required, TypeAlias, TypedDict
7
+
8
+ from .speech_model import SpeechModel
9
+
10
+ __all__ = ["SpeechCreateParams", "Voice", "VoiceID"]
11
+
12
+
13
+ class SpeechCreateParams(TypedDict, total=False):
14
+ input: Required[str]
15
+ """The text to generate audio for. The maximum length is 4096 characters."""
16
+
17
+ model: Required[Union[str, SpeechModel]]
18
+ """
19
+ One of the available [TTS models](https://platform.openai.com/docs/models#tts):
20
+ `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`.
21
+ """
22
+
23
+ voice: Required[Voice]
24
+ """The voice to use when generating the audio.
25
+
26
+ Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`,
27
+ `fable`, `onyx`, `nova`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. You
28
+ may also provide a custom voice object with an `id`, for example
29
+ `{ "id": "voice_1234" }`. Previews of the voices are available in the
30
+ [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options).
31
+ """
32
+
33
+ instructions: str
34
+ """Control the voice of your generated audio with additional instructions.
35
+
36
+ Does not work with `tts-1` or `tts-1-hd`.
37
+ """
38
+
39
+ response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"]
40
+ """The format to audio in.
41
+
42
+ Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`.
43
+ """
44
+
45
+ speed: float
46
+ """The speed of the generated audio.
47
+
48
+ Select a value from `0.25` to `4.0`. `1.0` is the default.
49
+ """
50
+
51
+ stream_format: Literal["sse", "audio"]
52
+ """The format to stream the audio in.
53
+
54
+ Supported formats are `sse` and `audio`. `sse` is not supported for `tts-1` or
55
+ `tts-1-hd`.
56
+ """
57
+
58
+
59
+ class VoiceID(TypedDict, total=False):
60
+ """Custom voice reference."""
61
+
62
+ id: Required[str]
63
+ """The custom voice ID, e.g. `voice_1234`."""
64
+
65
+
66
+ Voice: TypeAlias = Union[
67
+ str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], VoiceID
68
+ ]
.venv311/Lib/site-packages/openai/types/audio/speech_model.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from typing_extensions import Literal, TypeAlias
4
+
5
+ __all__ = ["SpeechModel"]
6
+
7
+ SpeechModel: TypeAlias = Literal["tts-1", "tts-1-hd", "gpt-4o-mini-tts", "gpt-4o-mini-tts-2025-12-15"]
.venv311/Lib/site-packages/openai/types/audio/transcription.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from typing import List, Union, Optional
4
+ from typing_extensions import Literal, Annotated, TypeAlias
5
+
6
+ from ..._utils import PropertyInfo
7
+ from ..._models import BaseModel
8
+
9
+ __all__ = ["Transcription", "Logprob", "Usage", "UsageTokens", "UsageTokensInputTokenDetails", "UsageDuration"]
10
+
11
+
12
+ class Logprob(BaseModel):
13
+ token: Optional[str] = None
14
+ """The token in the transcription."""
15
+
16
+ bytes: Optional[List[float]] = None
17
+ """The bytes of the token."""
18
+
19
+ logprob: Optional[float] = None
20
+ """The log probability of the token."""
21
+
22
+
23
+ class UsageTokensInputTokenDetails(BaseModel):
24
+ """Details about the input tokens billed for this request."""
25
+
26
+ audio_tokens: Optional[int] = None
27
+ """Number of audio tokens billed for this request."""
28
+
29
+ text_tokens: Optional[int] = None
30
+ """Number of text tokens billed for this request."""
31
+
32
+
33
+ class UsageTokens(BaseModel):
34
+ """Usage statistics for models billed by token usage."""
35
+
36
+ input_tokens: int
37
+ """Number of input tokens billed for this request."""
38
+
39
+ output_tokens: int
40
+ """Number of output tokens generated."""
41
+
42
+ total_tokens: int
43
+ """Total number of tokens used (input + output)."""
44
+
45
+ type: Literal["tokens"]
46
+ """The type of the usage object. Always `tokens` for this variant."""
47
+
48
+ input_token_details: Optional[UsageTokensInputTokenDetails] = None
49
+ """Details about the input tokens billed for this request."""
50
+
51
+
52
+ class UsageDuration(BaseModel):
53
+ """Usage statistics for models billed by audio input duration."""
54
+
55
+ seconds: float
56
+ """Duration of the input audio in seconds."""
57
+
58
+ type: Literal["duration"]
59
+ """The type of the usage object. Always `duration` for this variant."""
60
+
61
+
62
+ Usage: TypeAlias = Annotated[Union[UsageTokens, UsageDuration], PropertyInfo(discriminator="type")]
63
+
64
+
65
+ class Transcription(BaseModel):
66
+ """
67
+ Represents a transcription response returned by model, based on the provided input.
68
+ """
69
+
70
+ text: str
71
+ """The transcribed text."""
72
+
73
+ logprobs: Optional[List[Logprob]] = None
74
+ """The log probabilities of the tokens in the transcription.
75
+
76
+ Only returned with the models `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`
77
+ if `logprobs` is added to the `include` array.
78
+ """
79
+
80
+ usage: Optional[Usage] = None
81
+ """Token usage statistics for the request."""