Instructions to use sswayam/bt-momentum-engine with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Keras
How to use sswayam/bt-momentum-engine with Keras:
# Available backend options are: "jax", "torch", "tensorflow". import os os.environ["KERAS_BACKEND"] = "jax" import keras model = keras.saving.load_model("hf://sswayam/bt-momentum-engine") - Notebooks
- Google Colab
- Kaggle
Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .venv311/Lib/site-packages/openai-2.36.0.dist-info/INSTALLER +1 -0
- .venv311/Lib/site-packages/openai-2.36.0.dist-info/METADATA +1012 -0
- .venv311/Lib/site-packages/openai-2.36.0.dist-info/RECORD +0 -0
- .venv311/Lib/site-packages/openai-2.36.0.dist-info/REQUESTED +0 -0
- .venv311/Lib/site-packages/openai-2.36.0.dist-info/WHEEL +4 -0
- .venv311/Lib/site-packages/openai-2.36.0.dist-info/licenses/LICENSE +201 -0
- .venv311/Lib/site-packages/openai/_extras/__init__.py +3 -0
- .venv311/Lib/site-packages/openai/_extras/_common.py +21 -0
- .venv311/Lib/site-packages/openai/_extras/numpy_proxy.py +37 -0
- .venv311/Lib/site-packages/openai/_extras/pandas_proxy.py +28 -0
- .venv311/Lib/site-packages/openai/_extras/sounddevice_proxy.py +28 -0
- .venv311/Lib/site-packages/openai/_utils/__init__.py +67 -0
- .venv311/Lib/site-packages/openai/_utils/_compat.py +45 -0
- .venv311/Lib/site-packages/openai/_utils/_datetime_parse.py +136 -0
- .venv311/Lib/site-packages/openai/_utils/_json.py +35 -0
- .venv311/Lib/site-packages/openai/_utils/_logs.py +42 -0
- .venv311/Lib/site-packages/openai/_utils/_path.py +127 -0
- .venv311/Lib/site-packages/openai/_utils/_proxy.py +65 -0
- .venv311/Lib/site-packages/openai/_utils/_reflection.py +45 -0
- .venv311/Lib/site-packages/openai/_utils/_resources_proxy.py +24 -0
- .venv311/Lib/site-packages/openai/_utils/_streams.py +12 -0
- .venv311/Lib/site-packages/openai/_utils/_sync.py +58 -0
- .venv311/Lib/site-packages/openai/_utils/_transform.py +457 -0
- .venv311/Lib/site-packages/openai/_utils/_typing.py +156 -0
- .venv311/Lib/site-packages/openai/_utils/_utils.py +449 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/__init__.py +34 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/__init__.py +9 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_create_params.py +14 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_create_response.py +40 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_delete_response.py +18 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_list_params.py +24 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/groups/role_list_response.py +46 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/user_delete_response.py +15 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/user_update_params.py +15 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/users/__init__.py +9 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_create_params.py +14 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_create_response.py +22 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_delete_response.py +18 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_list_params.py +24 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/projects/users/role_list_response.py +46 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/users/__init__.py +9 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/users/role_create_params.py +12 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/users/role_create_response.py +22 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/users/role_delete_response.py +18 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/users/role_list_params.py +22 -0
- .venv311/Lib/site-packages/openai/types/admin/organization/users/role_list_response.py +46 -0
- .venv311/Lib/site-packages/openai/types/audio/__init__.py +23 -0
- .venv311/Lib/site-packages/openai/types/audio/speech_create_params.py +68 -0
- .venv311/Lib/site-packages/openai/types/audio/speech_model.py +7 -0
- .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 |
+
[)](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."""
|